Refunds

    Cancel or refund Stripe Terminal payments.

    Stripe Terminal uses a two-step process to prevent unintended and duplicate payments. When the SDK returns a confirmed PaymentIntent to your app, the payment is authorized, but not captured.

    If the PaymentIntent has already been captured, you must refund the underlying charge created by the PaymentIntent, using the refunds API on your server.

    Canceling payments Client-sideServer-side

    You can cancel a PaymentIntent at any time before it has been captured. Canceling a PaymentIntent releases all uncaptured funds, and a canceled PaymentIntent can no longer be used to perform charges.

    This can be useful if, for example, your customer decides to use a different payment method or pay with cash after the payment has been processed. In your application’s UI, consider allowing the user to cancel after processing the payment, before finalizing the payment and notifying your backend to capture.

    With the JavaScript SDK, you must cancel uncaptured payments on the server. The iOS or Android SDK let you cancel from the client side.

    // Action for a "Cancel" button
    func cancelAction() {
        if let paymentIntent = self.paymentIntent {
            Terminal.shared.cancelPaymentIntent(paymentIntent) { cancelResult, cancelError in
                if let error = cancelError {
                    print("cancelPaymentIntent failed: \(error)")
                }
                else {
                    print("cancelPaymentIntent succeeded")
                }
            }
        }
    }
    
    // Action for a "Cancel" button
    - (void)cancelAction {
        if (self.paymentIntent) {
            [[SCPTerminal shared] cancelPaymentIntent:self.paymentIntent completion:^(SCPPaymentIntent *cancelResult, NSError *cancelError) {
                if (cancelError) {
                    NSLog(@"cancelPaymentIntent failed: %@", cancelError);
                }
                else {
                    NSLog(@"cancelPaymentIntent succeeded");
                }
            }];
        }
    }
    
    Terminal.getInstance().cancelPaymentIntent(paymentIntent, object: PaymentIntentCallback {
        override fun onSuccess(paymentIntent: PaymentIntent) {
          // Placeholder for handling a successful cancelation
        }
    
        override fun onFailure(exception: TerminalException) {
          // Placeholder for handling a failed cancelation
        }
    })
    Terminal.getInstance().cancelPaymentIntent(paymentIntent, new PaymentIntentCallback() {
        @Override
        public void onSuccess(PaymentIntent paymentIntent) {
          // Placeholder for handling a successful cancelation
        }
    
        @Override
        public void onFailure(TerminalException exception) {
          // Placeholder for handling a failed cancelation
        }
    })

    Refunds Server-side

    When you use a PaymentIntent to collect payment from a customer, Stripe creates a charge behind the scenes. To refund the customer’s payment after the PaymentIntent has succeeded, create a refund using the PaymentIntent, which is equivalent to refunding the underlying charge. You can also optionally refund part of their payment by specifying an amount. You can perform refunds with the API or through the Dashboard.

    Note that this does not apply if you are using separate authorization and capture, and you wish to refund a PaymentIntent that has a status of requires_capture. In this case, the charge attached to the PaymentIntent is still uncaptured and cannot be refunded directly. You should cancel the PaymentIntent instead.

    curl https://api.stripe.com/v1/refunds \
    -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
    -d payment_intent=pi_Aabcxyz01aDfoo
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
    
    refund = Stripe::Refund.create({
        payment_intent: 'pi_Aabcxyz01aDfoo',
    })
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
    
    refund = stripe.Refund.create(
        payment_intent='pi_Aabcxyz01aDfoo',
    )
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc');
    
    $re = \Stripe\Refund::create([
      'payment_intent' => "pi_Aabcxyz01aDfoo",
    ]);
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc";
    
    Refund refund = Refund.create(RefundCreateParams.builder()
      .setPaymentIntent("pi_Aabcxyz01aDfoo")
      .build());
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc');
    
    (async () => {
      const refund = await stripe.refunds.create({
        payment_intent: "pi_Aabcxyz01aDfoo"
      });
    })();
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"
    
    refundParams := &stripe.RefundParams{
      PaymentIntent: "pi_Aabcxyz01aDfoo",
    }
    r, err := refund.New(refundParams)
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc";
    
    var refunds = new RefundService();
    var refundOptions = new RefundCreateOptions {
      PaymentIntent = "pi_Aabcxyz01aDfoo"
    };
    var refund = refunds.Create(refundOptions);
    

    To refund part of a PaymentIntent, provide an amount parameter, as an integer in cents (or the charge currency’s smallest currency unit):

    curl https://api.stripe.com/v1/refunds \
    -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
    -d payment_intent=pi_Aabcxyz01aDfoo \
    -d amount=1000
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
    
    refund = Stripe::Refund.create({
        amount: 1000,
        payment_intent: 'pi_Aabcxyz01aDfoo',
    })
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
    
    refund = stripe.Refund.create(
        amount=1000,
        payment_intent='pi_Aabcxyz01aDfoo',
    )
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc');
    
    $re = \Stripe\Refund::create([
      'amount' => 1000,
      'payment_intent' => "pi_Aabcxyz01aDfoo",
    ]);
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc";
    
    Refund refund = Refund.create(RefundCreateParams.builder()
      .setAmount(1000L)
      .setPaymentIntent("pi_Aabcxyz01aDfoo")
      .build());
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc');
    
    (async () => {
      const refund = await stripe.refunds.create({
        amount: 1000,
        payment_intent: "pi_Aabcxyz01aDfoo",
      });
    })();
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"
    
    refundParams := &stripe.RefundParams{
      Amount: 1000,
      PaymentIntent: "pi_Aabcxyz01aDfoo",
    }
    r, err := refund.New(refundParams)
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc";
    
    var refunds = new RefundService();
    var refundOptions = new RefundCreateOptions {
      PaymentIntent = "pi_Aabcxyz01aDfoo",
      Amount = 1000
    };
    var refund = refunds.Create(refundOptions);
    

    We recommend reconciling payments on your backend after a day’s activity to prevent unintended authorizations and uncollected funds.

    Next steps

    Congratulations! Next, you might want to configure the customer-facing checkout experience in your app.

    Was this page helpful?

    Thank you for helping improve Stripe's documentation. If you need help or have any questions, please consider contacting support.

    On this page