Sign in
Create account
Sign in
Home
Payments
Business operations
Financial services
Developer tools
Security
All products
Home
Payments
Business operations
Home
Payments
Business operations
Financial services
Developer tools
Support
Overview
Quickstart
Stripe CLI
Stripe for Visual Studio Code
Webhooks
File uploads
Error handling
Error codes
API
Keys
Libraries
Upgrades
Rate limits
Card testing
Expanding responses
Domains and IP addresses
Building With Stripe
Stripe.js and Elements
Prebuilt iOS UI
Prebuilt Android UI
Extensions
Plugins
Samples
Checklist
HomeDeveloper tools

iOS basic integration

Accept cards and Apple Pay with the iOS SDK's prebuilt UI.

Stripe is working on a new payments UI for mobile apps. If you want to check out the new UI, please register for the beta program and then see the Mobile Payments UI Integration Guide for integration details to get started. When we release the new UI, this page will be deprecated.

Use this integration if you want a prebuilt UI that:

  • Accepts credit cards and Apple Pay
  • Saves and displays cards for reuse
  • Supports limited customization of fonts and colors
  • Displays full-screen view controllers to collect payment details, shipping address, and shipping method:
STPPaymentOptionsViewController

STPPaymentOptionsViewController

STPAddCardViewController

STPAddCardViewController

STPShippingAddressViewController

STPShippingAddressViewController

These view controllers are also available to use individually—see the steps below for more details. This integration requires both server and client-side steps to implement.

Check out the example Basic Integration app and backend for a full implementation of this guide.

Set up Stripe
Client-side
Server-side

First, you need a Stripe account. Register now.

Server-side

This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:

Terminal
# Available as a gem sudo gem install stripe
Gemfile
# If you use bundler, you can add this line to your Gemfile gem 'stripe'

Client-side

The iOS SDK is open source, fully documented, and compatible with apps supporting iOS 11 or above.

  1. If you haven’t already, install the latest version of CocoaPods.
  2. If you don’t have an existing Podfile, run the following command to create one:
    Terminal
    pod init
  3. Add this line to your Podfile:
    Podfile
    pod 'Stripe'
  4. Run the following command:
    Terminal
    pod install
  5. Don’t forget to use the .xcworkspace file to open your project in Xcode, instead of the .xcodeproj file, from here on out.
  6. In the future, to update to the latest version of the SDK, just run:
    Terminal
    pod update Stripe

For details on the latest SDK release and past versions, see the Releases page on GitHub. To receive notifications when a new release is published, watch releases for the repository.

When your app starts, configure the SDK with your Stripe publishable key so that it can make requests to the Stripe API.

AppDelegate.swift
import UIKit import Stripe @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { StripeAPI.defaultPublishableKey =
"pk_test_TYooMQauvdEDq54NiTphI7jx"
// do any other necessary launch configuration return true } }

Use your test mode keys while you test and develop, and your live mode keys before you publish your app.

Set up an ephemeral key
Client-side
Server-side

In order for the SDK to save and retrieve credit cards for later use, create a single Stripe Customer object for each of your users. When you create a new user or account on your server, create a corresponding Customer object at the same time, even if you don’t collect payment information from your users when they sign up. This ensures that your application has a matching Customer for each user.

For security, the Customer API is not directly accessible from the client. Instead, your server provides the SDK with an ephemeral key—a short-lived API key with restricted access to the Customer API. You can think of an ephemeral key as a session, authorizing the SDK to retrieve and update a specific Customer object for the duration of the session.

Server-side

To provide an ephemeral key to the SDK, you’ll need to expose a new API endpoint on your backend. This endpoint should create an ephemeral key for the current Stripe customer, and return the key’s unmodified response as JSON. When the SDK requests an ephemeral key, it will specify the version of the Stripe API that it expects the response to come from. Your endpoint must accept an api_version parameter, and use the specified API version when creating the ephemeral key. This ensures that the SDK always receives the correct ephemeral key response from your backend. Consult our Example Backend to see this in practice.

Terminal
curl https://api.stripe.com/v1/ephemeral_keys \ -u
sk_test_4eC39HqLyjWDarjtT1zdp7dc
: \ -d "customer"="{{CUSTOMER_ID}}" \ -H "Stripe-Version: {{API_VERSION}}"

Client-side

In your app, conform to the STPCustomerEphemeralKeyProvider protocol by implementing its createCustomerKeyWithAPIVersion method. This method requests an ephemeral key from the endpoint you created on the backend.

When implementing this method, be sure to pass the apiVersion parameter along to your ephemeral keys endpoint. Consult the API client in our example app to see this in practice.

MyAPIClient.swift
View full sample
import Stripe class MyAPIClient: NSObject, STPCustomerEphemeralKeyProvider { func createCustomerKey(withAPIVersion apiVersion: String, completion: @escaping STPJSONResponseCompletionBlock) { let url = self.baseURL.appendingPathComponent("ephemeral_keys") var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)! urlComponents.queryItems = [URLQueryItem(name: "api_version", value: apiVersion)] var request = URLRequest(url: urlComponents.url!) request.httpMethod = "POST" let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in guard let response = response as? HTTPURLResponse, response.statusCode == 200, let data = data, let json = ((try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]) as [String : Any]??) else { completion(nil, error) return } completion(json, nil) }) task.resume() } }

Set up an STPCustomerContext
Client-side

Next, initialize an STPCustomerContext with the STPCustomerEphemeralKeyProvider you created in the previous step.

A CustomerSession talks to your backend to retrieve an ephemeral key for your Customer with its STPCustomerEphemeralKeyProvider, and uses that key to manage retrieving and updating the Customer’s payment methods on your behalf.

ViewController.swift
// MyAPIClient implements STPCustomerEphemeralKeyProvider (see above) let customerContext = STPCustomerContext(keyProvider: MyAPIClient())

To reduce load times, preload your customer’s information by initializing STPCustomerContext before they enter your payment flow.

If your current user logs out of the app and a new user logs in, create a new instance of STPCustomerContext or clear the cached customer using the provided clearCachedCustomer method. On your backend, create and return a new ephemeral key for the Customer object associated with the new user.

Set up an STPPaymentContext
Client-side

Once you’ve set up your customer context, you can use it to initialize STPPaymentContext, the core class of the integration. Conform a class to STPPaymentContextDelegate and assign it to the payment context’s delegate and hostViewController properties. We recommend using your app’s checkout screen UIViewController. In the next steps, you will implement the STPPaymentContext delegate methods.

You should also set the payment context’s paymentAmount property, which will be displayed to your user in the Apple Pay dialog (you can change this later, if the amount of the user’s purchase changes).

ViewController.swift
init() { self.paymentContext = STPPaymentContext(customerContext: customerContext) super.init(nibName: nil, bundle: nil) self.paymentContext.delegate = self self.paymentContext.hostViewController = self self.paymentContext.paymentAmount = 5000 // This is in cents, i.e. $50 USD }

Handle the user's payment method
Client-side

In your checkout screen, add a button to let the customer enter or change their payment method. When tapped, use STPPaymentContext to push or present an STPPaymentOptionsViewController on the payment context’s hostViewController.

ViewController.swift
// If you prefer a modal presentation func choosePaymentButtonTapped() { self.paymentContext.presentPaymentOptionsViewController() } // If you prefer a navigation transition func choosePaymentButtonTapped() { self.paymentContext.pushPaymentOptionsViewController() }
STPPaymentOptionsViewController

STPPaymentOptionsViewController

STPAddCardViewController

STPAddCardViewController

STPPaymentOptionsViewController uses STPCustomerContext to display a Customer’s payment methods. If there are no stored payment methods or the Add New Card button is tapped, STPAddCardViewController is displayed. You can also initialize and display these view controllers without using STPPaymentContext.

- paymentContextDidChange:

This STPPaymentContext delegate method triggers when the content of the payment context changes, like when the user selects a new payment method or enters shipping information. This is a good place to update your UI:

ViewController.swift
func paymentContextDidChange(_ paymentContext: STPPaymentContext) { self.activityIndicator.animating = paymentContext.loading self.paymentButton.enabled = paymentContext.selectedPaymentOption != nil self.paymentLabel.text = paymentContext.selectedPaymentOption?.label self.paymentIcon.image = paymentContext.selectedPaymentOption?.image }

Handle the user's shipping info
Client-side

If your user needs to enter or change their shipping address and shipping method, STPPaymentContext can do this for you automatically. STPPaymentContext will save shipping info to the Stripe customer when your user updates their information, and automatically prefill the shipping view controller for future purchases. Note that you should not rely on the shipping information stored on the Stripe customer for order fulfillment, as your user may change this information if they make multiple purchases. We recommend adding shipping information when you create a PaymentIntent object (which can also help prevent fraud), or when saving it to your own database. When presenting the shipping view controller, you can specify whether you’d like it presented modally, or pushed onto a UINavigationController stack:

ViewController.swift
// If you prefer a modal presentation func shippingButtonTapped() { self.paymentContext.presentShippingViewController() } // If you prefer a navigation transition func shippingButtonTapped() { self.paymentContext.pushShippingViewController() }

This sets up and presents an STPShippingAddressViewController on the payment context’s hostViewController. Once the user enters a valid shipping address, they’re taken to an STPShippingMethodsViewController. After they select a shipping method, both view controllers are dismissed or popped off the hostViewController's stack.

STPShippingAddressViewController

STPShippingAddressViewController

STPShippingMethodsController

STPShippingMethodsViewController

- paymentContext:didUpdateShippingAddress:completion:

This method is called after your user enters a shipping address. Validate the returned address and determine the shipping methods available for that address.

If the address is valid, call the provided completion block with a status of STPShippingStatusValid, nil for the error argument, an array of shipping methods, and a selected shipping method. If you don’t need to collect a shipping method, pass nil for the shipping methods and selected shipping method. If the address is invalid, call the completion block with a status of STPShippingStatusInvalid, an error object describing the issue with the address, and nil for the shipping methods and selected shipping method. Note that providing an error object is optional—if you omit it, the user will simply see an alert with the message “Invalid Shipping Address”.

ViewController.swift
func paymentContext(_ paymentContext: STPPaymentContext, didUpdateShippingAddress address: STPAddress, completion: @escaping STPShippingMethodsCompletionBlock) { let upsGround = PKShippingMethod() upsGround.amount = 0 upsGround.label = "UPS Ground" upsGround.detail = "Arrives in 3-5 days" upsGround.identifier = "ups_ground" let fedEx = PKShippingMethod() fedEx.amount = 5.99 fedEx.label = "FedEx" fedEx.detail = "Arrives tomorrow" fedEx.identifier = "fedex" if address.country == "US" { completion(.valid, nil, [upsGround, fedEx], upsGround) } else { completion(.invalid, nil, nil, nil) } }

Submit the payment
Client-side
Server-side

When your user is ready to pay (e.g., they tap the Buy button) call requestPayment on your payment context. It displays any required UI (such as the Apple Pay dialog) and calls the appropriate methods on its delegate as your user finishes their payment.

ViewController.swift
func payButtonTapped() { self.paymentContext.requestPayment() }

- paymentContext:didCreatePaymentResult:completion:

This method is called when the customer has successfully selected a payment method. Submit the payment to Stripe using a Payment Intent. Stripe uses this payment object to track and handle all the states of the payment until the payment completes.

Server-side

On your server, make an endpoint that creates a PaymentIntent with an amount and currency and returns its client secret to your client.

Always decide how much to charge on the server side, a trusted environment, as opposed to the client. This prevents malicious customers from being able to choose their own prices.

Terminal
curl https://api.stripe.com/v1/payment_intents \ -u
sk_test_4eC39HqLyjWDarjtT1zdp7dc
: \ -d "amount"=1099 \ -d "currency"="usd"

Client-side

On the client, implement this delegate method to:

  1. Request a PaymentIntent from your server
  2. Assemble a STPPaymentIntentParams object with the PaymentIntent client secret from your server and the paymentMethod provided by the delegate method.
  3. Call the STPPaymentHandler confirmPayment method to confirm the payment, passing the STPPaymentContext as the authenticationContext.
ViewController.swift
func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: @escaping STPErrorBlock) { // Request a PaymentIntent from your backend MyAPIClient.sharedClient.createPaymentIntent(products: self.products, shippingMethod: paymentContext.selectedShippingMethod) { result in switch result { case .success(let clientSecret): // Assemble the PaymentIntent parameters let paymentIntentParams = STPPaymentIntentParams(clientSecret: clientSecret) paymentIntentParams.paymentMethodId = paymentResult.paymentMethod.stripeId // Confirm the PaymentIntent STPPaymentHandler.shared().confirmPayment(withParams: paymentIntentParams, authenticationContext: paymentContext) { status, paymentIntent, error in switch status { case .succeeded: // Your backend asynchronously fulfills the customer's order, e.g. via webhook completion(.success, nil) case .failed: completion(.error, error) // Report error case .canceled: completion(.userCancellation, nil) // Customer cancelled @unknown default: completion(.error, nil) } } case .failure(let error): completion(.error, error) // Report error from your API break } } }

You must call the provided completion block with the appropriate STPPaymentStatus (.success, .error, or .userCancellation) when the customer’s payment is finished.

- paymentContext:didFinishWithStatus:error:

This method is called after the previous method, when any auxiliary UI that has been displayed (such as the Apple Pay dialog) has been dismissed. You should inspect the returned status and show an appropriate message to your user. For example:

ViewController.swift
func paymentContext(_ paymentContext: STPPaymentContext, didFinishWithStatus status: STPPaymentStatus, error: Error?) { switch status { case .error: self.showError(error) case .success: self.showReceipt() case .userCancellation: return // Do nothing } }

- paymentContext:didFailToLoadWithError:

This method is called in the rare case that the payment context’s initial loading call fails, usually due to lack of internet connectivity. You should dismiss your checkout page when this occurs and invite the user to try again. You can also optionally attempt to try again by calling retryLoading on the payment context.

ViewController.swift
func paymentContext(_ paymentContext: STPPaymentContext, didFailToLoadWithError error: Error) { self.navigationController?.popViewController(animated: true) // Show the error to your user, etc. }

Test the integration

​​Several test cards are available for you to use in test mode to make sure this integration is ready. Use them with any CVC and an expiration date in the future.

NumberDescription
Succeeds and immediately processes the payment.
Requires authentication. Stripe triggers a modal asking for the customer to authenticate.
Always fails with a decline code of insufficient_funds.

For the full list of test cards see our guide on testing.

OptionalHandle post-payment events

OptionalSet up Apple Pay
Client-side

OptionalCustomize the UI
Client-side

See also

  • After the payment
  • The Payment Intents API
  • Stripe iOS SDK Reference
Was this page helpful?
Questions? Contact us.
Developer tutorials on YouTube.
You can unsubscribe at any time. Read our privacy policy.
On this page
Set up Stripe
Set up an ephemeral key
Set up an STPCustomerContext
Set up an STPPaymentContext
Handle the user's payment method
Handle the user's shipping info
Submit the payment
Test the integration
Handle post-payment events
Set up Apple Pay
Customize the UI