Refunds Beta

    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 pay with cash after a payment has started.

    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, new PaymentIntentCallback() {
        // Placeholder for continuing after canceling the PaymentIntent
    })

    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 on the charge object. 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.

    To find the charge object, you can inspect the PaymentIntent’s charges property, which has an array of all the charges created by the PaymentIntent. The first charge in the array is the one that completed successfully.

    The following example shows how to refund a PaymentIntent’s charge:

    curl https://api.stripe.com/v1/payment_intents/pi_Aabcxyz01aDfoo \
    -u sk_test_4eC39HqLyjWDarjtT1zdp7dc:
    # Retrieve the ID of the first charge in PaymentIntent
    # charges array returned from the previous request and use
    # it to create a refund.
    curl https://api.stripe.com/v1/refunds \
    -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
    -d charge="{CHARGE_ID}"
    # 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'
    
    intent = Stripe::PaymentIntent.retrieve('pi_Aabcxyz01aDfoo')
    intent['charges']['data'].first.refund
    
    # 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'
    
    intent = stripe.PaymentIntent.retrieve("pi_Aabcxyz01aDfoo")
    intent['charges']['data'][0].refund()
    
    // 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');
    
    $intent = \Stripe\PaymentIntent::retrieve("pi_Aabcxyz01aDfoo");
    $intent->charges->data[0].refund();
    
    // 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";
    
    PaymentIntent intent = PaymentIntent.retrieve("pi_Aabcxyz01aDfoo");
    intent.charges.getData().get(0).refund(new Map());
    
    // 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 intent = await stripe.paymentIntents.retrieve("pi_Aabcxyz01aDfoo");
      const refund = await stripe.refunds.create({
        charge: intent.charges.data[0].id
      });
    })();
    
    // 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"
    
    intent, err := paymentintent.Get("pi_Aabcxyz01aDfoo", nil)
    refundParams := &stripe.RefundParams{
      Charge: stripe.String(intent.Charges.Data[0].ID),
    }
    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.SetApiKey("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
    
    var paymentIntents = new PaymentIntentService();
    var refunds = new RefundService();
    var intent = paymentIntents.Get("pi_Aabcxyz01aDfoo");
    var refundOptions = new RefundCreateOptions {
      ChargeId = intent.Charges.Data[0].Id
    };
    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.

    Questions?

    We're always happy to help with code or other questions you might have. Search our documentation, contact support, or connect with our sales team. You can also chat live with other developers in #stripe on freenode.

    Was this page helpful? Yes No

    Send

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

    On this page