Sign in
An image of the Stripe logo
Create account
Sign in
Home
Payments
Business operations
Financial services
Developer tools
No-code
All products
Home
Payments
Business operations
Home
Payments
Business operations
Financial services
Developer tools
Overview
Developer tools
    Get started
    Quickstarts
    Stripe Shell
    Stripe CLI
    Dashboard
    Stripe for Visual Studio Code
    Webhooks
    File uploads
    Error handling
    Security at Stripe
    API
    API keys
    Upgrades
    Changelog
    Rate limits
    Automated testing
    Data Availability
    Expanding responses
    Domains and IP addresses
    Search
    Building With Stripe
    Prebuilt iOS UI
    Prebuilt Android UI
    Extensions
    Samples
    Checklist
    Feedback
SDKs
Sample projects
Videos
Stripe Apps
Stripe Connectors
Partners
HomeDeveloper tools

iOS basic integration

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

We created an improved payments UI for mobile apps with features such as additional payment methods and SwiftUI support. We recommend using it for your integration instead of this one.

If you want to migrate but are unable to, please let us know.

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:

Command Line
# For detailed setup, see our quickstarts at https://stripe.com/docs/development/quickstart bundle add stripe

Client-side

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

To install the SDK, follow these steps:

  1. In Xcode, select File > Add Packages… and enter https://github.com/stripe/stripe-ios-spm as the repository URL.
  2. Select the latest version number from our releases page.
  3. Add the Stripe product to the target of your app.

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 when 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.

Command Line
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, that is, 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 sees 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 (for example, 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.

Command Line
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(paymentIntentParams, with: paymentContext) { status, paymentIntent, error in switch status { case .succeeded: // Your backend asynchronously fulfills the customer's order, for example, via webhook completion(.success, nil) case .failed: completion(.error, error) // Report error case .canceled: completion(.userCancellation, nil) // Customer canceled @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, and so on }

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?
Need help? Contact Support.
Watch our developer tutorials.
Check out our product changelog.
Questions? Contact Sales.
Powered by Markdoc
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
See also
Stripe Shell
Test mode
Welcome to the Stripe Shell! Stripe Shell is a browser-based shell with the Stripe CLI pre-installed. Login to your Stripe account and press Control + Backtick on your keyboard to start managing your Stripe resources in test mode. - View supported Stripe commands: - Find webhook events: - Listen for webhook events: - Call Stripe APIs: stripe [api resource] [operation] (e.g. )
The Stripe Shell is best experienced on desktop.
$