# Collect card payments

Prepare your application and back end to collect card payments using Stripe Terminal.

# iOS


New to the Payment Intents API? Here are some helpful resources:

- [The Payment Intents API](https://docs.stripe.com/payments/payment-intents.md)
- [The PaymentIntent object](https://docs.stripe.com/api/payment_intents.md)
- [More payment scenarios](https://docs.stripe.com/payments/more-payment-scenarios.md)

Collecting payments with Stripe Terminal requires writing a payment flow in your application. Use the Stripe Terminal SDK to create and update a [PaymentIntent](https://docs.stripe.com/api.md#payment_intents), an object representing a single payment session.

Designed to be robust to failures, the Terminal integration splits the payment process into several steps, each of which can be retried safely:

1. [Create a PaymentIntent](https://docs.stripe.com/terminal/payments/collect-card-payment.md#create-payment).
2. [Process the payment](https://docs.stripe.com/terminal/payments/collect-card-payment.md#process-payment). Authorization on the customer’s card takes place when the SDK processes the payment.
3. (Optional) [Capture the payment](https://docs.stripe.com/terminal/payments/collect-card-payment.md#capture-payment)

## Create a PaymentIntent [Client-side] [Server-side]

The first step when collecting payments is to start the payment flow. When a customer begins checking out, your application must create a `PaymentIntent` object. This represents a new payment session on Stripe.

- [createPaymentIntent (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPTerminal.html#/c:objc\(cs\)SCPTerminal\(im\)createPaymentIntent:completion:)

You can create a `PaymentIntent` on the client or server.

Use [test amounts](https://docs.stripe.com/terminal/references/testing.md#physical-test-cards) to try producing different results. An amount ending in `00` results in an approved payment.

> #### Don't recreate PaymentIntents for declined cards
> 
> Don’t recreate a PaymentIntent if a card is declined. Instead, re-use the same PaymentIntent to help [avoid double charges](https://docs.stripe.com/terminal/payments/collect-card-payment.md#avoiding-double-charges).

### Client-side 

Create a `PaymentIntent` from your client:

#### Swift

```swift
import UIKit
import StripeTerminal

class PaymentViewController: UIViewController {

    // ...

    // Action for a "Checkout" button
    func checkoutAction() throws {
        let params = try PaymentIntentParametersBuilder(amount: 1000, currency: "usd").build()
        Terminal.shared.createPaymentIntent(params) { createResult, createError in
            if let error = createError {
                print("createPaymentIntent failed: \(error)")
            } else if let paymentIntent = createResult {
                print("createPaymentIntent succeeded")
                // ...
            }

        }
    }

    // ...
}
```

### Server-side 

You can create the `PaymentIntent` on your server if the information required to start a payment isn’t readily available in your app.

The following example shows how to create a `PaymentIntent` on your server:

#### curl

```bash
curl https://api.stripe.com/v1/payment_intents \
  -u <<YOUR_SECRET_KEY>>: \
  -d "amount"=1000 \
  -d "currency"="usd" \
  -d "payment_method_types[]"="card_present" \
  -d "capture_method"="manual"
```

For Terminal payments, the `payment_method_types` parameter must include `card_present`.

You can control the payment flow as follows:

- To fully control the payment flow for `card_present` payments, set the `capture_method` to `manual`. This allows you to add a reconciliation step before finalizing the payment.
- To authorize and capture payments in one step, set the `capture_method` to `automatic`.

To accept payments in Australia, you need to set `capture_method` to `automatic` or `manual_preferred`. For more details, visit our [Australia documentation](https://docs.stripe.com/terminal/payments/regional.md?integration-country=AU). To accept Interac payments in Canada, you must also include `interac_present` in `payment_method_types`. For more details, visit our [Canada documentation](https://docs.stripe.com/terminal/payments/regional.md?integration-country=CA).

The `PaymentIntent` contains a [client secret](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-client_secret), a key that’s unique to the individual `PaymentIntent`. To use the client secret, you must obtain it from the `PaymentIntent` on your server and [pass it to the client side](https://docs.stripe.com/payments/payment-intents.md#passing-to-client).

#### Ruby

```ruby
post '/create_payment_intent' do
  intent = # ... Create or retrieve the PaymentIntent
  {client_secret: intent.client_secret}.to_json
end
```

- [retrievePaymentIntent (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPTerminal.html#/c:objc\(cs\)SCPTerminal\(im\)retrievePaymentIntent:completion:)

To retrieve a `PaymentIntent`, use the client secret to call `retrievePaymentIntent`.

After you retrieve the `PaymentIntent`, use it to call `processPaymentIntent`.

#### Swift

```swift
func checkoutButtonAction() {
    // ... Fetch the client secret from your backend
    Terminal.shared.retrievePaymentIntent(clientSecret: clientSecret) { retrieveResult, retrieveError in
        if let error = retrieveError {
            print("retrievePaymentIntent failed: \(error)")
        }
        else if let paymentIntent = retrieveResult {
            print("retrievePaymentIntent succeeded: \(paymentIntent)")
            // ...
        }
    }
}
```

## Process the payment [Client-side]

You can process a payment immediately with the card presented by a customer, or instead inspect card details before proceeding to process the payment. For most use cases, we recommend processing immediately, because it’s a simpler integration with fewer API calls. However, if you want to insert your own business logic before authorizing the card, use the two-step collect-and-confirm flow.

#### Process immediately

After you create a PaymentIntent, the next step is to process the payment. The reader prompts the customer to insert or tap their card and then attempts to authorize the payment.

- [processPaymentIntent (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPTerminal.html#/c:objc\(cs\)SCPTerminal\(im\)processPaymentIntent:delegate:completion:)

While processing a payment, cardholder might take a few seconds to get their card from their wallet or pose a question to the operator during payment.

#### Swift

```swift


// Action for a "Checkout" button
func checkoutAction() throws {
  let params = try PaymentIntentParametersBuilder(amount: 1000, currency: "usd").build()
  Terminal.shared.createPaymentIntent(params) { createResult, createError in
      if let error = createError {
          print("createPaymentIntent failed: \(error)")
      } else if let paymentIntent = createResult {
          print("createPaymentIntent succeeded")
          self.processCancelable = Terminal.shared.processPaymentIntent(paymentIntent, collectConfig: nil, confirmConfig: nil) { processResult, processError in
              if let error = processError {
                  print("processPaymentIntent failed: \(error)")
              } else if let processedPaymentIntent = processResult {
                  print("processPaymentIntent succeeded")
                  // Notify your backend to capture the PaymentIntent
                  if let stripeId = processedPaymentIntent.stripeId {
                      APIClient.shared.capturePaymentIntent(stripeId) { captureError in
                          if let error = captureError {
                              print("capturePaymentIntent failed: \(error)")
                          } else {
                              print("capturePaymentIntent succeeded")
                          }
                      }
                  } else {
                      print("Payment processed offline");
                  }
              }
          }
      }
  }
}
```

### Cancel collection 

#### Programmatic cancellation

- [Cancelable (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPCancelable.html)

You can cancel processing a PaymentIntent using the `Cancelable` object returned by the iOS SDK.

#### Customer-initiated cancellation

- [setCustomerCancellation (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPCollectPaymentIntentConfigurationBuilder.html#/c:objc\(cs\)SCPCollectPaymentIntentConfigurationBuilder\(im\)setCustomerCancellation)
- [CustomerCancellation (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPCustomerCancellation.html)

Smart readers show customers a cancel button by default. You can disable this by setting `customerCancellation` to `.disableIfAvailable`.

Tapping the cancel button cancels the active transaction.

#### Swift

```swift

let collectConfig = try CollectPaymentIntentConfigurationBuilder()
    .setCustomerCancellation(.disableIfAvailable) // turn OFF the cancel button, ON by default
    .build()
Terminal.shared.collectPaymentMethod(paymentIntent: paymentIntent, collectConfig: collectConfig) {
    intentWithPaymentMethod, attachError in
}
```

### Handle events 

- [ReaderDisplayDelegate (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Protocols/SCPReaderDisplayDelegate.html)

When collecting a payment method using a reader such as the [Stripe M2](https://docs.stripe.com/terminal/readers/stripe-m2.md), without a built-in display, your app must be able to display events from the payment method collection process to users. These events help users successfully collect payments (for example, retrying a card, trying a different card, or using a different read method).

When a transaction begins, the SDK passes a `ReaderInputOptions` value to your app’s reader display handler, denoting the acceptable types of input (for example, `Swipe`, `Insert`, or `Tap`). In your app’s checkout UI, prompt the user to present a card using one of these options.

During the transaction, the SDK might request your app to display additional prompts (for example, `Retry Card`) to your user by passing a `ReaderDisplayMessage` value to your app’s reader display handler. Make sure your checkout UI displays these messages to the user.

#### Swift

```swift
 // MARK: MobileReaderDelegate - only needed for Bluetooth readers, this is the delegate set during connectReader

 func reader(_ reader: Reader, didRequestReaderInput inputOptions: ReaderInputOptions) {
     readerMessageLabel.text = Terminal.stringFromReaderInputOptions(inputOptions)
 }

 func reader(_ reader: Reader, didRequestReaderDisplayMessage displayMessage: ReaderDisplayMessage) {
     readerMessageLabel.text = Terminal.stringFromReaderDisplayMessage(displayMessage)
 }
```

### Collect payments with Tap to Pay on iPhone 

When your application is ready to collect a payment, the Stripe Terminal iOS SDK takes over the display to handle the collection process. After calling the [process payment](https://docs.stripe.com/terminal/payments/collect-card-payment.md#process-payment) method, your application remains running, but the iPhone displays a full-screen prompt to the cardholder, instructing them to present their card or NFC-based mobile wallet. If there’s an error reading the card, a prompt for retry displays. A successful presentation returns a success indication, and then control returns to your application.
![Tap to Pay on iPhone payment collection](https://b.stripecdn.com/docs-statics-srv/assets/ttpoi-payment-collection.50a552f2d75b8a3b92a439810cd9361d.png)

Payment collection

- For manual capture of payments, a successful `processPaymentIntent` call results in a `PaymentIntent` with a status of `requires_capture`.
- For automatic capture of payments, the `PaymentIntent` transitions to a `succeeded` state.

You must manually capture a PaymentIntent within 2 days or the authorization expires and funds are released to the customer.

### Handle failures 

- [ConfirmPaymentIntentError (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPConfirmPaymentIntentError.html#/c:objc\(cs\)SCPConfirmPaymentIntentError\(py\)paymentIntent)

When processing a payment fails, the SDK returns an error that includes the updated `PaymentIntent` if it was declined by stripe. Your application needs to inspect the `PaymentIntent` to decide how to deal with the error.

| PaymentIntent status      | Meaning                                                     | Resolution                                                                                                                                      |
| ------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `requires_payment_method` | Payment method declined                                     | Try collecting a different payment method by calling `processPaymentIntent` again with the same `PaymentIntent`.                                |
| `requires_confirmation`   | Temporary connectivity problem                              | Call `processPaymentIntent` again with the same `PaymentIntent` to retry the request.                                                           |
| `PaymentIntent` is `nil`  | Request to Stripe timed out, unknown `PaymentIntent` status | Retry processing the original `PaymentIntent`. Don’t create a new one, because that might result in multiple authorizations for the cardholder. |

If you encounter multiple, consecutive timeouts, there might be a problem with your connectivity. Make sure that your app can communicate with the internet.

### Avoid double charges 

The `PaymentIntent` object enables money movement at Stripe—use a single `PaymentIntent` to represent a transaction.

Re-use the same `PaymentIntent` after a card is declined (for example, if it has insufficient funds), so your customer can try again with a different card.

If you edit the `PaymentIntent`, you must call `processPaymentIntent` to update the payment information on the reader.

A `PaymentIntent` must be in the `requires_payment_method` state before Stripe can process it. An authorized, captured, or canceled `PaymentIntent` can’t be processed by a reader.

#### Collect, inspect, and confirm

After you create a PaymentIntent, the next step is to process the payment. The reader prompts the customer to insert or tap their card and then creates a PaymentMethod.

## Collect a PaymentMethod

- [collectPaymentMethod (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPTerminal.html#/c:objc\(cs\)SCPTerminal\(im\)collectPaymentMethod:delegate:completion:)

After you’ve created a `PaymentIntent`, the next step is to collect a payment method with the SDK.

To collect a payment method, your app needs to be connected to a reader. The connected reader waits for a card to be presented after your app calls `collectPaymentMethod`.

#### Swift

```swift


import UIKit
import StripeTerminal

class PaymentViewController: UIViewController, ReaderDisplayDelegate {


    // Label for displaying messages from the card reader
    let readerMessageLabel = UILabel(frame: .zero)
    var collectCancelable: Cancelable? = nil

    // ...

    // Action for a "Checkout" button
    func checkoutAction() throws {
        let params = try PaymentIntentParametersBuilder(amount: 1000, currency: "usd").build()
        Terminal.shared.createPaymentIntent(params) { createResult, createError in
            if let error = createError {
                print("createPaymentIntent failed: \(error)")
            }
            else if let paymentIntent = createResult {
                print("createPaymentIntent succeeded")
                self.collectCancelable = Terminal.shared.collectPaymentMethod(paymentIntent) { collectResult, collectError in
                    if let error = collectError {
                        print("collectPaymentMethod failed: \(error)")
                    }
                    else if let paymentIntent = collectResult {
                        print("collectPaymentMethod succeeded")
                        // ... Confirm the payment
                    }
                }
            }

        }
    }
 }

 // MARK: MobileReaderDelegate - only needed for mobile readers, this is the delegate set during connectReader

 func reader(_ reader: Reader, didRequestReaderInput inputOptions: ReaderInputOptions = []) {
     readerMessageLabel.text = Terminal.stringFromReaderInputOptions(inputOptions)
 }

 func reader(_ reader: Reader, didRequestReaderDisplayMessage displayMessage: ReaderDisplayMessage) {
     readerMessageLabel.text = Terminal.stringFromReaderDisplayMessage(displayMessage)
 }
 // MARK: ReaderDisplayDelegate

 func terminal(_ terminal: Terminal, didRequestReaderInput inputOptions: ReaderInputOptions = []) {
     readerMessageLabel.text = Terminal.stringFromReaderInputOptions(inputOptions)
 }

 func terminal(_ terminal: Terminal, didRequestReaderDisplayMessage displayMessage: ReaderDisplayMessage) {
     readerMessageLabel.text = Terminal.stringFromReaderDisplayMessage(displayMessage)
 }
```

This method collects encrypted payment method data using the connected card reader, and associates the encrypted data with the local `PaymentIntent`.

### Optionally inspect payment method details 

- [CollectPaymentIntentConfiguration (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPCollectPaymentIntentConfiguration.html)
- [CardPresentDetails (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPCardPresentDetails.html)

For advanced use cases, you can examine the payment method details of the presented card and perform your own business logic prior to authorization.

Use the `setUpdatePaymentIntent` setter in `CollectPaymentIntentConfigurationBuilder` to attach a `PaymentMethod` to the server-side `PaymentIntent`. This data is returned in the `collectPaymentMethod` response.   

#### Swift

```swift


class PaymentViewController: UIViewController, ReaderDisplayDelegate {
    // ...

    // Action for a "Checkout" button
    func checkoutAction() throws {
        let params = try PaymentIntentParametersBuilder(amount: 1000, currency: "usd").build()
        Terminal.shared.createPaymentIntent(params) { createResult, createError in
            if let error = createError {
                print("createPaymentIntent failed: \(error)")
            }
            else if let paymentIntent = createResult {
                print("createPaymentIntent succeeded")
                let collectConfig = try CollectPaymentIntentConfigurationBuilder().setUpdatePaymentIntent(true).build()
                self.collectCancelable = Terminal.shared.collectPaymentMethod(paymentIntent: paymentIntent, collectConfig: collectConfig) {
                  collectResult, collectError in
                    if let error = collectError {
                        print("collectPaymentMethod failed: \(error)")
                    }
                    else if let paymentIntent = collectResult {
                        print("collectPaymentMethod succeeded")
                        if let paymentMethod = paymentIntent.paymentMethod,
                            let card = paymentMethod.cardPresent ?? paymentMethod.interacPresent {

                            // ... Perform business logic on card
                        }

                        // ... Confirm the payment
                    }
                }
            }

        }
    }
 }
```

This method attaches the collected encrypted payment method data with an update to the `PaymentIntent` object. It doesn’t require authorization until you confirm the payment.

After payment method collection, you must authorize or cancel the payment within 30 seconds.

If the SDK is [operating offline](https://docs.stripe.com/terminal/features/operate-offline/collect-card-payments.md), the `paymentMethod` field isn’t present in the `PaymentIntent` object.

You can access attributes like card brand, funding, and other useful data at this point.

Stripe attempts to detect whether a mobile wallet is used in a transaction as shown in the `wallet.type` attribute. However, the attribute isn’t populated if the card’s issuing bank doesn’t support reader-driven identification of a mobile wallet, so accurate detection isn’t guaranteed. After authorization and when you [process the payment](https://docs.stripe.com/terminal/payments/collect-card-payment.md#process-payment), Stripe receives up-to-date information from the networks and updates `wallet.type`.

### Cancel collection 

#### Programmatic cancellation

- [Cancelable (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPCancelable.html)

You can cancel collecting a payment method using the `Cancelable` object returned by the iOS SDK.

#### Customer-initiated cancellation

- [setCustomerCancellation (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPCollectPaymentIntentConfigurationBuilder.html#/c:objc\(cs\)SCPCollectPaymentIntentConfigurationBuilder\(im\)setCustomerCancellation)
- [CustomerCancellation (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPCustomerCancellation.html)

Smart readers show customers a cancel button by default. You can disable this by setting `customerCancellation` to `.disableIfAvailable`.

Tapping the cancel button cancels the active transaction.

#### Swift

```swift

let collectConfig = try CollectPaymentIntentConfigurationBuilder()
    .setCustomerCancellation(.disableIfAvailable) // turn OFF the cancel button, ON by default
    .build()
Terminal.shared.collectPaymentMethod(paymentIntent: paymentIntent, collectConfig: collectConfig) {
    intentWithPaymentMethod, attachError in
}
```

### Handle events 

- [ReaderDisplayDelegate (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Protocols/SCPReaderDisplayDelegate.html)

When collecting a payment method using a reader such as the [Stripe M2](https://docs.stripe.com/terminal/readers/stripe-m2.md), without a built-in display, your app must be able to display events from the payment method collection process to users. These events help users successfully collect payments (for example, retrying a card, trying a different card, or using a different read method).

When a transaction begins, the SDK passes a `ReaderInputOptions` value to your app’s reader display handler, denoting the acceptable types of input (for example, `Swipe`, `Insert`, or `Tap`). In your app’s checkout UI, prompt the user to present a card using one of these options.

During the transaction, the SDK might request your app to display additional prompts (for example, `Retry Card`) to your user by passing a `ReaderDisplayMessage` value to your app’s reader display handler. Make sure your checkout UI displays these messages to the user.

#### Swift

```swift
 // MARK: MobileReaderDelegate - only needed for Bluetooth readers, this is the delegate set during connectReader

 func reader(_ reader: Reader, didRequestReaderInput inputOptions: ReaderInputOptions) {
     readerMessageLabel.text = Terminal.stringFromReaderInputOptions(inputOptions)
 }

 func reader(_ reader: Reader, didRequestReaderDisplayMessage displayMessage: ReaderDisplayMessage) {
     readerMessageLabel.text = Terminal.stringFromReaderDisplayMessage(displayMessage)
 }
```

### Collect payments with Tap to Pay on iPhone 

When your application is ready to collect a payment, the Stripe Terminal iOS SDK takes over the display to handle the collection process. After calling the [process payment](https://docs.stripe.com/terminal/payments/collect-card-payment.md#process-payment) method, your application remains running, but the iPhone displays a full-screen prompt to the cardholder, instructing them to present their card or NFC-based mobile wallet. If there’s an error reading the card, a prompt for retry displays. A successful presentation returns a success indication, and then control returns to your application.
![Tap to Pay on iPhone payment collection](https://b.stripecdn.com/docs-statics-srv/assets/ttpoi-payment-collection.50a552f2d75b8a3b92a439810cd9361d.png)

Payment collection

## Confirm the PaymentIntent

- [confirmPaymentIntent (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPTerminal.html#/c:objc\(cs\)SCPTerminal\(im\)confirmPaymentIntent:completion:)

After successfully collecting a payment method from the customer, the next step is to confirm the payment with the SDK. When you’re ready to proceed with the payment, call `confirmPaymentIntent` with the updated `PaymentIntent` from the [previous step](https://docs.stripe.com/terminal/payments/collect-card-payment.md#collect-inspect-payment-method).

- For manual capture of payments, a successful `confirmPaymentIntent` call results in a `PaymentIntent` with a status of `requires_capture`.
- For automatic capture of payments, the `PaymentIntent` transitions to a `succeeded` state.

Always confirm PaymentIntents using the Terminal SDK on the client. Server-side confirmation bypasses critical interactions, such as PIN prompts, and might result in transaction failures.

#### Swift

```swift


// Action for a "Checkout" button
func checkoutAction() throws {
  let params = try PaymentIntentParametersBuilder(amount: 1000, currency: "usd").build()
  Terminal.shared.createPaymentIntent(params) { createResult, createError in
      if let error = createError {
          print("createPaymentIntent failed: \(error)")
      } else if let paymentIntent = createResult {
          print("createPaymentIntent succeeded")
          self.collectCancelable = Terminal.shared.collectPaymentMethod(paymentIntent) { collectResult, collectError in
              if let error = collectError {
                  print("collectPaymentMethod failed: \(error)")
              } else if let collectPaymentMethodPaymentIntent = collectResult {
                  print("collectPaymentMethod succeeded")
                  // ... Confirm the payment
                  self.confirmCancelable = Terminal.shared.confirmPaymentIntent(collectPaymentMethodPaymentIntent) { confirmResult, confirmError in
                      if let error = confirmError {
                          print("confirmPaymentIntent failed: \(error)")
                      } else if let confirmedPaymentIntent = confirmResult {
                          print("confirmPaymentIntent succeeded")
                          // Notify your backend to capture the PaymentIntent
                          if let stripeId = confirmedPaymentIntent.stripeId {
                              APIClient.shared.capturePaymentIntent(stripeId) { captureError in
                                  if let error = captureError {
                                      print("capture failed: \(error)")
                                  } else {
                                      print("capture succeeded")
                                  }
                              }
                          } else {
                              print("Payment collected offline");
                          }
                      }
                  }
              }
          }
      }
  }
```

You must manually capture a PaymentIntent within 2 days or the authorization expires and funds are released to the customer.

### Handle failures 

- [ConfirmPaymentIntentError (iOS)](https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPConfirmPaymentIntentError.html#/c:objc\(cs\)SCPConfirmPaymentIntentError\(py\)paymentIntent)

When confirming a payment fails, the SDK returns an error that includes the updated `PaymentIntent`. Your application needs to inspect the `PaymentIntent` to decide how to deal with the error.

| PaymentIntent Status      | Meaning                                                     | Resolution                                                                                                                                      |
| ------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `requires_payment_method` | Payment method declined                                     | Try collecting a different payment method by calling `collectPaymentMethod` again with the same `PaymentIntent`.                                |
| `requires_confirmation`   | Temporary connectivity problem                              | Call `confirmPaymentIntent` again with the same `PaymentIntent` to retry the request.                                                           |
| `PaymentIntent` is `nil`  | Request to Stripe timed out, unknown `PaymentIntent` status | Retry confirming the original `PaymentIntent`. Don’t create a new one, because that might result in multiple authorizations for the cardholder. |

If you encounter multiple, consecutive timeouts, there might be a problem with your connectivity. Make sure that your app can communicate with the internet.

### Avoid double charges 

The `PaymentIntent` object enables money movement at Stripe—use a single `PaymentIntent` to represent a transaction.

Re-use the same `PaymentIntent` after a card is declined (for example, if it has insufficient funds), so your customer can try again with a different card.

If you edit the `PaymentIntent`, you must call `collectPaymentMethod` to update the payment information on the reader.

A `PaymentIntent` must be in the `requires_payment_method` state before Stripe can confirm it. An authorized, captured, or canceled `PaymentIntent` can’t be confirmed by a reader.

## Capture the payment [Server-side]

If you defined `capture_method` as `manual` during `PaymentIntent` creation in [Step 1](https://docs.stripe.com/terminal/payments/collect-card-payment.md#create-payment), the SDK returns an authorized but not captured `PaymentIntent` to your application. Learn more about the difference between [authorization and capture](https://docs.stripe.com/payments/place-a-hold-on-a-payment-method.md).

When your app receives a confirmed `PaymentIntent` from the SDK, make sure it notifies your backend to capture the payment. Create an endpoint on your backend that accepts a `PaymentIntent` ID and sends a request to the Stripe API to capture it:

```curl
curl -X POST https://api.stripe.com/v1/payment_intents/{{PAYMENT_INTENT_ID}}/capture \
  -u "<<YOUR_SECRET_KEY>>:"
```

A successful `capture` call results in a `PaymentIntent` with a status of `succeeded`.

To make sure the application fee captured is correct for connected accounts, inspect each `PaymentIntent` and modify the application fee, if needed, before manually capturing the payment.

### Reconcile payments 

To monitor the payments activity of your business, you might want to reconcile PaymentIntents with your internal orders system on your server at the end of a day’s activity.

A `PaymentIntent` that retains a `requires_capture` status might represent two things:

**Unnecessary authorization on your customer’s card statement**

- Cause: User abandons your app’s checkout flow in the middle of a transaction
- Solution: If the uncaptured `PaymentIntent` isn’t associated with a completed order on your server, you can [cancel](https://docs.stripe.com/api/payment_intents/cancel.md) it. You can’t use a canceled `PaymentIntent` to perform charges.

**Incomplete collection of funds from a customer**

- Cause: Failure of the request from your app notifying your backend to capture the payment
- Solution: If the uncaptured `PaymentIntent` is associated with a completed order on your server, and no other payment has been taken for the order (for example, a cash payment), you can [capture](https://docs.stripe.com/api/payment_intents/capture.md) it.

### Collect tips (US only)

In the US, eligible users can [collect a tip on the receipt when capturing payments](https://docs.stripe.com/terminal/features/collecting-tips/on-receipt.md).

