/stripe-guides

How to programmatically issue refunds with Stripe API?

Learn how to programmatically issue full or partial refunds with the Stripe API, including setup, code examples, error handling, webhooks, and best practices.

Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

Book a free consultation

How to programmatically issue refunds with Stripe API?

How to Programmatically Issue Refunds with Stripe API

 

Step 1: Set Up Your Stripe Account and Get API Keys

 

Before you can start issuing refunds programmatically, you need to set up your Stripe account and obtain your API keys:

  • Create a Stripe account at stripe.com if you don't already have one.
  • Go to the Stripe Dashboard and navigate to Developers → API keys.
  • You'll see two types of keys: Publishable and Secret. For refunds, you'll need the Secret key.
  • Make sure to use test keys for development and only switch to live keys when your implementation is ready for production.

 

Step 2: Install the Stripe Library

 

Install the Stripe library for your programming language. Below are examples for popular languages:

For Node.js:

npm install stripe

For Python:

pip install stripe

For PHP:

composer require stripe/stripe-php

For Ruby:

gem install stripe

 

Step 3: Initialize the Stripe Client

 

Once you've installed the library, you need to initialize the Stripe client with your secret key.

Node.js:

const stripe = require('stripe')('sk_test_your_secret_key');

// Or with ES modules
// import Stripe from 'stripe';
// const stripe = new Stripe('sk_test_your_secret_key');

Python:

import stripe
stripe.api_key = "sk_test_your_secret\_key"

PHP:

\Stripe\Stripe::setApiKey('sk_test_your_secret_key');

Ruby:

require 'stripe'
Stripe.api_key = 'sk_test_your_secret\_key'

 

Step 4: Issue a Full Refund

 

To issue a full refund, you need the payment intent or charge ID that you want to refund.

Node.js:

async function issueFullRefund(paymentIntentId) {
  try {
    const refund = await stripe.refunds.create({
      payment\_intent: paymentIntentId,
    });
    console.log('Refund processed successfully:', refund.id);
    return refund;
  } catch (error) {
    console.error('Error processing refund:', error);
    throw error;
  }
}

Python:

def issue_full_refund(payment_intent_id):
    try:
        refund = stripe.Refund.create(
            payment_intent=payment_intent\_id
        )
        print(f"Refund processed successfully: {refund.id}")
        return refund
    except Exception as e:
        print(f"Error processing refund: {str(e)}")
        raise

PHP:

function issueFullRefund($paymentIntentId) {
    try {
        $refund = \Stripe\Refund::create([
            'payment\_intent' => $paymentIntentId,
        ]);
        echo "Refund processed successfully: " . $refund->id;
        return $refund;
    } catch (\Exception $e) {
        echo "Error processing refund: " . $e->getMessage();
        throw $e;
    }
}

Ruby:

def issue_full_refund(payment_intent_id)
  begin
    refund = Stripe::Refund.create({
      payment_intent: payment_intent\_id
    })
    puts "Refund processed successfully: #{refund.id}"
    return refund
  rescue Stripe::StripeError => e
    puts "Error processing refund: #{e.message}"
    raise e
  end
end

 

Step 5: Issue a Partial Refund

 

For partial refunds, you need to specify the amount to refund in addition to the payment intent or charge ID.

Node.js:

async function issuePartialRefund(paymentIntentId, amountToRefund) {
  try {
    // Amount should be in cents (e.g., $10.00 = 1000)
    const refund = await stripe.refunds.create({
      payment\_intent: paymentIntentId,
      amount: amountToRefund,
    });
    console.log('Partial refund processed successfully:', refund.id);
    return refund;
  } catch (error) {
    console.error('Error processing partial refund:', error);
    throw error;
  }
}

Python:

def issue_partial_refund(payment_intent_id, amount_to_refund):
    try:
        # Amount should be in cents (e.g., $10.00 = 1000)
        refund = stripe.Refund.create(
            payment_intent=payment_intent\_id,
            amount=amount_to_refund
        )
        print(f"Partial refund processed successfully: {refund.id}")
        return refund
    except Exception as e:
        print(f"Error processing partial refund: {str(e)}")
        raise

PHP:

function issuePartialRefund($paymentIntentId, $amountToRefund) {
    try {
        // Amount should be in cents (e.g., $10.00 = 1000)
        $refund = \Stripe\Refund::create([
            'payment\_intent' => $paymentIntentId,
            'amount' => $amountToRefund,
        ]);
        echo "Partial refund processed successfully: " . $refund->id;
        return $refund;
    } catch (\Exception $e) {
        echo "Error processing partial refund: " . $e->getMessage();
        throw $e;
    }
}

Ruby:

def issue_partial_refund(payment_intent_id, amount_to_refund)
  begin
    # Amount should be in cents (e.g., $10.00 = 1000)
    refund = Stripe::Refund.create({
      payment_intent: payment_intent\_id,
      amount: amount_to_refund
    })
    puts "Partial refund processed successfully: #{refund.id}"
    return refund
  rescue Stripe::StripeError => e
    puts "Error processing partial refund: #{e.message}"
    raise e
  end
end

 

Step 6: Add a Reason for the Refund

 

Adding a reason for the refund can be helpful for bookkeeping and auditing purposes. Stripe allows you to specify one of the following reasons: 'duplicate', 'fraudulent', or 'requested_by_customer'.

Node.js:

async function issueRefundWithReason(paymentIntentId, reason) {
  try {
    const refund = await stripe.refunds.create({
      payment\_intent: paymentIntentId,
      reason: reason, // 'duplicate', 'fraudulent', or 'requested_by_customer'
    });
    console.log('Refund processed with reason:', refund.id);
    return refund;
  } catch (error) {
    console.error('Error processing refund:', error);
    throw error;
  }
}

Python:

def issue_refund_with_reason(payment_intent\_id, reason):
    try:
        refund = stripe.Refund.create(
            payment_intent=payment_intent\_id,
            reason=reason  # 'duplicate', 'fraudulent', or 'requested_by_customer'
        )
        print(f"Refund processed with reason: {refund.id}")
        return refund
    except Exception as e:
        print(f"Error processing refund: {str(e)}")
        raise

PHP:

function issueRefundWithReason($paymentIntentId, $reason) {
    try {
        $refund = \Stripe\Refund::create([
            'payment\_intent' => $paymentIntentId,
            'reason' => $reason, // 'duplicate', 'fraudulent', or 'requested_by_customer'
        ]);
        echo "Refund processed with reason: " . $refund->id;
        return $refund;
    } catch (\Exception $e) {
        echo "Error processing refund: " . $e->getMessage();
        throw $e;
    }
}

Ruby:

def issue_refund_with_reason(payment_intent\_id, reason)
  begin
    refund = Stripe::Refund.create({
      payment_intent: payment_intent\_id,
      reason: reason  # 'duplicate', 'fraudulent', or 'requested_by_customer'
    })
    puts "Refund processed with reason: #{refund.id}"
    return refund
  rescue Stripe::StripeError => e
    puts "Error processing refund: #{e.message}"
    raise e
  end
end

 

Step 7: Refund Specific Payment Intent or Charge

 

You can refund either using a payment intent ID or a charge ID. Here's how to refund using a charge ID:

Node.js:

async function refundByChargeId(chargeId) {
  try {
    const refund = await stripe.refunds.create({
      charge: chargeId,
    });
    console.log('Refund processed using charge ID:', refund.id);
    return refund;
  } catch (error) {
    console.error('Error processing refund:', error);
    throw error;
  }
}

Python:

def refund_by_charge_id(charge_id):
    try:
        refund = stripe.Refund.create(
            charge=charge\_id
        )
        print(f"Refund processed using charge ID: {refund.id}")
        return refund
    except Exception as e:
        print(f"Error processing refund: {str(e)}")
        raise

PHP:

function refundByChargeId($chargeId) {
    try {
        $refund = \Stripe\Refund::create([
            'charge' => $chargeId,
        ]);
        echo "Refund processed using charge ID: " . $refund->id;
        return $refund;
    } catch (\Exception $e) {
        echo "Error processing refund: " . $e->getMessage();
        throw $e;
    }
}

Ruby:

def refund_by_charge_id(charge_id)
  begin
    refund = Stripe::Refund.create({
      charge: charge\_id
    })
    puts "Refund processed using charge ID: #{refund.id}"
    return refund
  rescue Stripe::StripeError => e
    puts "Error processing refund: #{e.message}"
    raise e
  end
end

 

Step 8: Add Metadata to a Refund

 

Metadata can be used to store additional information about the refund for your records.

Node.js:

async function refundWithMetadata(paymentIntentId, metadata) {
  try {
    const refund = await stripe.refunds.create({
      payment\_intent: paymentIntentId,
      metadata: metadata, // e.g., { order_id: '12345', customer_email: '[email protected]' }
    });
    console.log('Refund processed with metadata:', refund.id);
    return refund;
  } catch (error) {
    console.error('Error processing refund:', error);
    throw error;
  }
}

Python:

def refund_with_metadata(payment_intent_id, metadata):
    try:
        refund = stripe.Refund.create(
            payment_intent=payment_intent\_id,
            metadata=metadata  # e.g., { 'order_id': '12345', 'customer_email': '[email protected]' }
        )
        print(f"Refund processed with metadata: {refund.id}")
        return refund
    except Exception as e:
        print(f"Error processing refund: {str(e)}")
        raise

PHP:

function refundWithMetadata($paymentIntentId, $metadata) {
    try {
        $refund = \Stripe\Refund::create([
            'payment\_intent' => $paymentIntentId,
            'metadata' => $metadata, // e.g., ['order_id' => '12345', 'customer_email' => '[email protected]']
        ]);
        echo "Refund processed with metadata: " . $refund->id;
        return $refund;
    } catch (\Exception $e) {
        echo "Error processing refund: " . $e->getMessage();
        throw $e;
    }
}

Ruby:

def refund_with_metadata(payment_intent_id, metadata)
  begin
    refund = Stripe::Refund.create({
      payment_intent: payment_intent\_id,
      metadata: metadata  # e.g., { order_id: '12345', customer_email: '[email protected]' }
    })
    puts "Refund processed with metadata: #{refund.id}"
    return refund
  rescue Stripe::StripeError => e
    puts "Error processing refund: #{e.message}"
    raise e
  end
end

 

Step 9: Retrieve a Refund

 

After processing a refund, you might want to retrieve it to check its status or get additional information.

Node.js:

async function retrieveRefund(refundId) {
  try {
    const refund = await stripe.refunds.retrieve(refundId);
    console.log('Refund retrieved:', refund);
    return refund;
  } catch (error) {
    console.error('Error retrieving refund:', error);
    throw error;
  }
}

Python:

def retrieve_refund(refund_id):
    try:
        refund = stripe.Refund.retrieve(refund\_id)
        print(f"Refund retrieved: {refund}")
        return refund
    except Exception as e:
        print(f"Error retrieving refund: {str(e)}")
        raise

PHP:

function retrieveRefund($refundId) {
    try {
        $refund = \Stripe\Refund::retrieve($refundId);
        echo "Refund retrieved: " . json\_encode($refund);
        return $refund;
    } catch (\Exception $e) {
        echo "Error retrieving refund: " . $e->getMessage();
        throw $e;
    }
}

Ruby:

def retrieve_refund(refund_id)
  begin
    refund = Stripe::Refund.retrieve(refund\_id)
    puts "Refund retrieved: #{refund}"
    return refund
  rescue Stripe::StripeError => e
    puts "Error retrieving refund: #{e.message}"
    raise e
  end
end

 

Step 10: List All Refunds

 

You might also want to list all refunds or filter them based on certain criteria.

Node.js:

async function listRefunds(limit = 10) {
  try {
    const refunds = await stripe.refunds.list({
      limit: limit,
    });
    console.log('Refunds listed:', refunds.data.length);
    return refunds;
  } catch (error) {
    console.error('Error listing refunds:', error);
    throw error;
  }
}

Python:

def list\_refunds(limit=10):
    try:
        refunds = stripe.Refund.list(limit=limit)
        print(f"Refunds listed: {len(refunds.data)}")
        return refunds
    except Exception as e:
        print(f"Error listing refunds: {str(e)}")
        raise

PHP:

function listRefunds($limit = 10) {
    try {
        $refunds = \Stripe\Refund::all(['limit' => $limit]);
        echo "Refunds listed: " . count($refunds->data);
        return $refunds;
    } catch (\Exception $e) {
        echo "Error listing refunds: " . $e->getMessage();
        throw $e;
    }
}

Ruby:

def list\_refunds(limit=10)
  begin
    refunds = Stripe::Refund.list(limit: limit)
    puts "Refunds listed: #{refunds.data.length}"
    return refunds
  rescue Stripe::StripeError => e
    puts "Error listing refunds: #{e.message}"
    raise e
  end
end

 

Step 11: Update a Refund

 

You can update some information associated with a refund, such as metadata.

Node.js:

async function updateRefundMetadata(refundId, metadata) {
  try {
    const refund = await stripe.refunds.update(refundId, {
      metadata: metadata,
    });
    console.log('Refund metadata updated:', refund.id);
    return refund;
  } catch (error) {
    console.error('Error updating refund metadata:', error);
    throw error;
  }
}

Python:

def update_refund_metadata(refund\_id, metadata):
    try:
        refund = stripe.Refund.modify(
            refund\_id,
            metadata=metadata
        )
        print(f"Refund metadata updated: {refund.id}")
        return refund
    except Exception as e:
        print(f"Error updating refund metadata: {str(e)}")
        raise

PHP:

function updateRefundMetadata($refundId, $metadata) {
    try {
        $refund = \Stripe\Refund::update($refundId, [
            'metadata' => $metadata,
        ]);
        echo "Refund metadata updated: " . $refund->id;
        return $refund;
    } catch (\Exception $e) {
        echo "Error updating refund metadata: " . $e->getMessage();
        throw $e;
    }
}

Ruby:

def update_refund_metadata(refund\_id, metadata)
  begin
    refund = Stripe::Refund.update(
      refund\_id,
      { metadata: metadata }
    )
    puts "Refund metadata updated: #{refund.id}"
    return refund
  rescue Stripe::StripeError => e
    puts "Error updating refund metadata: #{e.message}"
    raise e
  end
end

 

Step 12: Handle Webhooks for Refund Events

 

To keep your system in sync with Stripe, you should set up webhook handling for refund events.

Node.js with Express:

const express = require('express');
const stripe = require('stripe')('sk_test_your_secret_key');
const bodyParser = require('body-parser');

const app = express();

// Use JSON parser for webhook endpoint
app.post('/webhook', bodyParser.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;

  try {
    event = stripe.webhooks.constructEvent(
      req.body,
      sig,
      'whsec_your_webhook_signing_secret'
    );
  } catch (err) {
    console.error(`Webhook Error: ${err.message}`);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  // Handle the event
  switch (event.type) {
    case 'charge.refunded':
      const chargeRefunded = event.data.object;
      console.log('Charge refunded:', chargeRefunded.id);
      // Update your database with refund information
      break;
    case 'refund.created':
      const refundCreated = event.data.object;
      console.log('Refund created:', refundCreated.id);
      // Process the refund creation
      break;
    case 'refund.updated':
      const refundUpdated = event.data.object;
      console.log('Refund updated:', refundUpdated.id);
      // Process the refund update
      break;
    default:
      console.log(`Unhandled event type ${event.type}`);
  }

  // Return a response to acknowledge receipt of the event
  res.json({ received: true });
});

app.listen(3000, () => {
  console.log('Webhook server running on port 3000');
});

Python with Flask:

import stripe
from flask import Flask, request, jsonify

app = Flask(**name**)
stripe.api_key = "sk_test_your_secret\_key"
webhook_secret = "whsec_your_webhook_signing\_secret"

@app.route('/webhook', methods=['POST'])
def webhook():
    payload = request.data
    sig\_header = request.headers.get('Stripe-Signature')

    try:
        event = stripe.Webhook.construct\_event(
            payload, sig_header, webhook_secret
        )
    except ValueError as e:
        # Invalid payload
        return jsonify(error=str(e)), 400
    except stripe.error.SignatureVerificationError as e:
        # Invalid signature
        return jsonify(error=str(e)), 400

    # Handle the event
    if event['type'] == 'charge.refunded':
        charge\_refunded = event\['data']\['object']
        print(f"Charge refunded: {charge\_refunded['id']}")
        # Update your database with refund information
    elif event['type'] == 'refund.created':
        refund\_created = event\['data']\['object']
        print(f"Refund created: {refund\_created['id']}")
        # Process the refund creation
    elif event['type'] == 'refund.updated':
        refund\_updated = event\['data']\['object']
        print(f"Refund updated: {refund\_updated['id']}")
        # Process the refund update
    else:
        print(f"Unhandled event type {event['type']}")

    return jsonify(success=True), 200

if **name** == '**main**':
    app.run(port=3000)

 

Step 13: Error Handling and Best Practices

 

Proper error handling is essential when working with the Stripe API. Here's a more comprehensive error handling approach:

Node.js:

async function safeRefund(paymentIntentId, amount = null) {
  try {
    // Verify the payment intent exists and is in a refundable state
    const paymentIntent = await stripe.paymentIntents.retrieve(paymentIntentId);
    
    if (paymentIntent.status !== 'succeeded') {
      throw new Error(`Payment intent ${paymentIntentId} is not in a refundable state. Status: ${paymentIntent.status}`);
    }
    
    // Check if it's already refunded
    if (paymentIntent.charges.data[0].refunded) {
      console.log(`Payment intent ${paymentIntentId} is already refunded.`);
      return { already\_refunded: true };
    }
    
    // Create refund parameters
    const refundParams = {
      payment\_intent: paymentIntentId,
    };
    
    // Add amount if it's a partial refund
    if (amount) {
      refundParams.amount = amount;
    }
    
    // Process refund
    const refund = await stripe.refunds.create(refundParams);
    console.log(`Refund processed successfully: ${refund.id}`);
    
    return refund;
  } catch (error) {
    if (error.type === 'StripeInvalidRequestError') {
      console.error(`Invalid request error: ${error.message}`);
    } else if (error.type === 'StripeAPIError') {
      console.error(`API error: ${error.message}`);
    } else if (error.type === 'StripeAuthenticationError') {
      console.error(`Authentication error: ${error.message}`);
    } else {
      console.error(`Unexpected error: ${error.message}`);
    }
    
    throw error;
  }
}

Python:

def safe_refund(payment_intent\_id, amount=None):
    try:
        # Verify the payment intent exists and is in a refundable state
        payment_intent = stripe.PaymentIntent.retrieve(payment_intent\_id)
        
        if payment\_intent.status != 'succeeded':
            raise ValueError(f"Payment intent {payment_intent_id} is not in a refundable state. Status: {payment\_intent.status}")
        
        # Check if it's already refunded
        if payment\_intent.charges.data[0].refunded:
            print(f"Payment intent {payment_intent_id} is already refunded.")
            return {"already\_refunded": True}
        
        # Create refund parameters
        refund\_params = {
            "payment_intent": payment_intent\_id
        }
        
        # Add amount if it's a partial refund
        if amount:
            refund\_params["amount"] = amount
        
        # Process refund
        refund = stripe.Refund.create(\*\*refund\_params)
        print(f"Refund processed successfully: {refund.id}")
        
        return refund
    except stripe.error.InvalidRequestError as e:
        print(f"Invalid request error: {str(e)}")
        raise
    except stripe.error.APIError as e:
        print(f"API error: {str(e)}")
        raise
    except stripe.error.AuthenticationError as e:
        print(f"Authentication error: {str(e)}")
        raise
    except Exception as e:
        print(f"Unexpected error: {str(e)}")
        raise

 

Step 14: Implementing Idempotency

 

Idempotency ensures that you don't accidentally process the same refund twice if you retry a failed request.

Node.js:

async function idempotentRefund(paymentIntentId, idempotencyKey) {
  try {
    const refund = await stripe.refunds.create(
      {
        payment\_intent: paymentIntentId,
      },
      {
        idempotencyKey: idempotencyKey, // Use a unique key for each refund request
      }
    );
    console.log('Idempotent refund processed:', refund.id);
    return refund;
  } catch (error) {
    console.error('Error processing refund:', error);
    throw error;
  }
}

Python:

def idempotent_refund(payment_intent_id, idempotency_key):
    try:
        refund = stripe.Refund.create(
            payment_intent=payment_intent\_id,
            idempotency_key=idempotency_key  # Use a unique key for each refund request
        )
        print(f"Idempotent refund processed: {refund.id}")
        return refund
    except Exception as e:
        print(f"Error processing refund: {str(e)}")
        raise

PHP:

function idempotentRefund($paymentIntentId, $idempotencyKey) {
    try {
        $refund = \Stripe\Refund::create(
            [
                'payment\_intent' => $paymentIntentId,
            ],
            [
                'idempotency\_key' => $idempotencyKey, // Use a unique key for each refund request
            ]
        );
        echo "Idempotent refund processed: " . $refund->id;
        return $refund;
    } catch (\Exception $e) {
        echo "Error processing refund: " . $e->getMessage();
        throw $e;
    }
}

Ruby:

def idempotent_refund(payment_intent_id, idempotency_key)
  begin
    refund = Stripe::Refund.create(
      { payment_intent: payment_intent\_id },
      { idempotency_key: idempotency_key } # Use a unique key for each refund request
    )
    puts "Idempotent refund processed: #{refund.id}"
    return refund
  rescue Stripe::StripeError => e
    puts "Error processing refund: #{e.message}"
    raise e
  end
end

 

Step 15: Integration with Your Backend System

 

Here's an example of how to integrate refunds with a REST API in a web application:

Node.js with Express:

const express = require('express');
const stripe = require('stripe')('sk_test_your_secret_key');
const bodyParser = require('body-parser');
const { v4: uuidv4 } = require('uuid');

const app = express();
app.use(bodyParser.json());

// Endpoint to process a refund
app.post('/api/refunds', async (req, res) => {
  const { payment_intent_id, amount, reason, metadata } = req.body;
  
  if (!payment_intent_id) {
    return res.status(400).json({ error: 'payment_intent_id is required' });
  }
  
  try {
    // Generate a unique idempotency key
    const idempotencyKey = uuidv4();
    
    // Prepare refund parameters
    const refundParams = {
      payment_intent: payment_intent\_id,
    };
    
    if (amount) refundParams.amount = amount;
    if (reason) refundParams.reason = reason;
    if (metadata) refundParams.metadata = metadata;
    
    // Process the refund with idempotency key
    const refund = await stripe.refunds.create(
      refundParams,
      { idempotencyKey }
    );
    
    // Record the refund in your database
    // await saveRefundToDatabase(refund);
    
    return res.status(200).json({
      success: true,
      refund\_id: refund.id,
      status: refund.status,
      amount: refund.amount,
    });
  } catch (error) {
    console.error('Refund error:', error);
    
    return res.status(error.statusCode || 500).json({
      success: false,
      error: error.message,
    });
  }
});

// Endpoint to get refund details
app.get('/api/refunds/:refund\_id', async (req, res) => {
  const { refund\_id } = req.params;
  
  try {
    const refund = await stripe.refunds.retrieve(refund\_id);
    
    return res.status(200).json({
      success: true,
      refund,
    });
  } catch (error) {
    console.error('Error retrieving refund:', error);
    
    return res.status(error.statusCode || 500).json({
      success: false,
      error: error.message,
    });
  }
});

// Start the server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Python with Flask:

import uuid
from flask import Flask, request, jsonify
import stripe

app = Flask(**name**)
stripe.api_key = "sk_test_your_secret\_key"

Endpoint to process a refund
@app.route('/api/refunds', methods=['POST'])
def create\_refund():
    data = request.get\_json()
    payment_intent_id = data.get('payment_intent_id')
    
    if not payment_intent_id:
        return jsonify({"error": "payment_intent_id is required"}), 400
    
    amount = data.get('amount')
    reason = data.get('reason')
    metadata = data.get('metadata')
    
    try:
        # Generate a unique idempotency key
        idempotency\_key = str(uuid.uuid4())
        
        # Prepare refund parameters
        refund\_params = {
            "payment_intent": payment_intent\_id
        }
        
        if amount:
            refund\_params["amount"] = amount
        if reason:
            refund\_params["reason"] = reason
        if metadata:
            refund\_params["metadata"] = metadata
        
        # Process the refund with idempotency key
        refund = stripe.Refund.create(
            \*\*refund\_params,
            idempotency_key=idempotency_key
        )
        
        # Record the refund in your database
        # save_refund_to\_database(refund)
        
        return jsonify({
            "success": True,
            "refund\_id": refund.id,
            "status": refund.status,
            "amount": refund.amount
        }), 200
    except stripe.error.StripeError as e:
        return jsonify({
            "success": False,
            "error": str(e)
        }), e.http\_status or 500
    except Exception as e:
        return jsonify({
            "success": False,
            "error": str(e)
        }), 500

Endpoint to get refund details
@app.route('/api/refunds/', methods=['GET'])
def get_refund(refund_id):
    try:
        refund = stripe.Refund.retrieve(refund\_id)
        
        return jsonify({
            "success": True,
            "refund": refund
        }), 200
    except stripe.error.StripeError as e:
        return jsonify({
            "success": False,
            "error": str(e)
        }), e.http\_status or 500
    except Exception as e:
        return jsonify({
            "success": False,
            "error": str(e)
        }), 500

if **name** == '**main**':
    app.run(port=3000, debug=True)

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Book a Free Consultation

Client trust and success are our top priorities

When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.

Rapid Dev was an exceptional project management organization and the best development collaborators I've had the pleasure of working with. They do complex work on extremely fast timelines and effectively manage the testing and pre-launch process to deliver the best possible product. I'm extremely impressed with their execution ability.

CPO, Praction - Arkady Sokolov

May 2, 2023

Working with Matt was comparable to having another co-founder on the team, but without the commitment or cost. He has a strategic mindset and willing to change the scope of the project in real time based on the needs of the client. A true strategic thought partner!

Co-Founder, Arc - Donald Muir

Dec 27, 2022

Rapid Dev are 10/10, excellent communicators - the best I've ever encountered in the tech dev space. They always go the extra mile, they genuinely care, they respond quickly, they're flexible, adaptable and their enthusiasm is amazing.

Co-CEO, Grantify - Mat Westergreen-Thorne

Oct 15, 2022

Rapid Dev is an excellent developer for no-code and low-code solutions.
We’ve had great success since launching the platform in November 2023. In a few months, we’ve gained over 1,000 new active users. We’ve also secured several dozen bookings on the platform and seen about 70% new user month-over-month growth since the launch.

Co-Founder, Church Real Estate Marketplace - Emmanuel Brown

May 1, 2024 

Matt’s dedication to executing our vision and his commitment to the project deadline were impressive. 
This was such a specific project, and Matt really delivered. We worked with a really fast turnaround, and he always delivered. The site was a perfect prop for us!

Production Manager, Media Production Company - Samantha Fekete

Sep 23, 2022