Developer tools
Stripe's UI libraries
Prebuilt iOS UI

iOS basic integration

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

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:

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.

1 Set up Stripe Client-side Server-side

First, you need a Stripe account. Register now.


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:

# Available as a gem gem install stripe
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
# Install through pip pip install --upgrade stripe
# Or find the Stripe package on
# Find the version you want to pin: # # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0
# Install the PHP library via Composer composer require stripe/stripe-php
# Or download the source directly:
/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - or - */ implementation "com.stripe:stripe-java:{VERSION}"
<!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - or - --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency>
# For other environments, manually install the following JARs: # - The Stripe JAR from # - Google Gson from
# Install via npm npm install --save stripe
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u
// Then import the package import ( "" )
# Install via dotnet dotnet add package dotnet restore
# Or install via NuGet PM> Install-Package


The iOS SDK is open source, fully documented, and compatible with apps supporting iOS 10 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:
    pod init
  3. Add this line to your Podfile:
    pod 'Stripe'
  4. Run the following command:
    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:
    pod update Stripe
  1. If you haven't already, install the latest version of Carthage.
  2. Add this line to your Cartfile:
    github "stripe/stripe-ios"
  3. Follow the Carthage installation instructions.
  4. In the future, to update to the latest version of the SDK, run the following command:
    carthage update stripe-ios --platform ios
  1. Head to our GitHub releases page and download and unzip
  2. Drag Stripe.framework to the "Embedded Binaries" section of your Xcode project's "General" settings. Make sure to select "Copy items if needed".
  3. Head to the "Build Phases" section of your Xcode project settings, and create a new "Run Script Build Phase". Paste the following snippet into the text field:
    bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Stripe.framework/"
  4. In the future, to update to the latest version of our SDK, just repeat steps 1 and 2.

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

import UIKit import Stripe @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { Stripe.setDefaultPublishableKey("pk_test_TYooMQauvdEDq54NiTphI7jx") // do any other necessary launch configuration return true } }
#import "AppDelegate.h" #import <Stripe/Stripe.h> @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [Stripe setDefaultPublishableKey:@"pk_test_TYooMQauvdEDq54NiTphI7jx"]; // do any other necessary launch configuration return YES; } @end

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


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.

curl \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d customer="{{CUSTOMER_ID}}" \ -H "Stripe-Version: {{API_VERSION}}"
key = Stripe::EphemeralKey.create( {customer: '{{CUSTOMER_ID}}'}, {stripe_version: '{{API_VERSION}}'} )
key = stripe.EphemeralKey.create(customer='{{CUSTOMER_ID}}', stripe_version='{{API_VERSION}}')
$key = \Stripe\EphemeralKey::create( ['customer' => '{{CUSTOMER_ID}}'], ['stripe_version' => '{{API_VERSION}}'] );
let key = await stripe.ephemeralKeys.create( {customer: '{{CUSTOMER_ID}}'}, {stripe_version: '{{API_VERSION}}'} );
RequestOptions requestOptions = (new RequestOptions.RequestOptionsBuilder()) .setStripeVersion("{{API_VERSION}}") .build(); Map<String, Object> options = new HashMap<String, Object>(); options.put("customer", "{{CUSTOMER_ID}}"); EphemeralKey key = EphemeralKey.create(options, requestOptions);
params := &stripe.EphemeralKeyParams{ Customer: stripe.String("{{CUSTOMER_ID}}"), StripeVersion: stripe.String("{{API_VERSION}}"), } key, _ := ephemeralkey.New(params)
var options = new EphemeralKeyCreateOptions { Customer = "{{CUSTOMER_ID}}", StripeVersion = "{{API_VERSION}}" }; var service = new EphemeralKeyService(); service.Create(options);


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.

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() } }
// In MyApiClient.h, declare conformance to STPCustomerEphemeralKeyProvider: // @interface MyAPIClient : NSObject <STPCustomerEphemeralKeyProvider> @implementation MyAPIClient - (void)createCustomerKeyWithAPIVersion:(NSString *)apiVersion completion:(STPJSONResponseCompletionBlock)completion { NSURL *url = [self.baseURL URLByAppendingPathComponent:@"ephemeral_keys"]; NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; urlComponents.queryItems = @[[[NSURLQueryItem alloc] initWithName:@"api_version" value:apiVersion]]; NSURLRequest *request = [[NSURLRequest alloc] initWithURL:urlComponents.URL]; NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (data != nil && [response isKindOfClass:[NSHTTPURLResponse class]] && ((NSHTTPURLResponse *)response).statusCode == 200) { NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; completion(json, nil); } else { completion(nil, error); } }]; [task resume]; } @end

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

// MyAPIClient implements STPCustomerEphemeralKeyProvider (see above) let customerContext = STPCustomerContext(keyProvider: MyAPIClient())
// MyAPIClient implements STPCustomerEphemeralKeyProvider (see above) STPCustomerContext *customerContext = [[STPCustomerContext alloc] initWithKeyProvider:[MyAPIClient new]];

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.

4 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).

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 }
- (instancetype)init { self = [super initWithNibName:nil bundle:nil]; if (self) { self.paymentContext = [[STPPaymentContext alloc] initWithCustomerContext:customerContext]; self.paymentContext.delegate = self; self.paymentContext.hostViewController = self; self.paymentContext.paymentAmount = 5000; // This in cents, i.e. $50 USD } return self; }

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

// If you prefer a modal presentation func choosePaymentButtonTapped() { self.paymentContext.presentPaymentOptionsViewController() } // If you prefer a navigation transition func choosePaymentButtonTapped() { self.paymentContext.pushPaymentOptionsViewController() }
// If you prefer a modal presentation - (void)choosePaymentButtonTapped { [self.paymentContext presentPaymentOptionsViewController]; } // If you prefer a navigation transition - (void)choosePaymentButtonTapped { [self.paymentContext pushPaymentOptionsViewController]; }

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:

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 }
- (void)paymentContextDidChange:(STPPaymentContext *)paymentContext { self.activityIndicator.animating = paymentContext.loading; self.paymentButton.enabled = paymentContext.selectedPaymentOption != nil; self.paymentLabel.text = paymentContext.selectedPaymentOption.label; self.paymentIcon.image = paymentContext.selectedPaymentOption.image; }

6 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:

// If you prefer a modal presentation func shippingButtonTapped() { self.paymentContext.presentShippingViewController() } // If you prefer a navigation transition func shippingButtonTapped() { self.paymentContext.pushShippingViewController() }
// If you prefer a modal presentation - (void)shippingButtonTapped { [self.paymentContext presentShippingViewController]; } // If you prefer a navigation transition - (void)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'll be taken to an STPShippingMethodsViewController. After they select a shipping method, both view controllers will be dismissed or popped off the hostViewController's stack.

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

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 == "US" { completion(.valid, nil, [upsGround, fedEx], upsGround) } else { completion(.invalid, nil, nil, nil) } }
- (void)paymentContext:(STPPaymentContext *)paymentContext didUpdateShippingAddress:(STPAddress *)address completion:(STPShippingMethodsCompletionBlock)completion { PKShippingMethod *upsGround = [PKShippingMethod new]; upsGround.amount = [NSDecimalNumber decimalNumberWithString:@"0"]; upsGround.label = @"UPS Ground"; upsGround.detail = @"Arrives in 3-5 days"; upsGround.identifier = @"ups_ground"; PKShippingMethod *fedEx = [PKShippingMethod new]; fedEx.amount = [NSDecimalNumber decimalNumberWithString:@"5.99"]; fedEx.label = @"FedEx"; fedEx.detail = @"Arrives tomorrow"; fedEx.identifier = @"fedex"; if ([ isEqualToString:@"US"]) { completion(STPShippingStatusValid, nil, @[upsGround, fedEx], upsGround); } else { completion(STPShippingStatusInvalid, nil, nil, nil); } }

7 Submit the payment Client-side Server-side

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

func payButtonTapped() { self.paymentContext.requestPayment() }
- (void)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.


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.

curl \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=usd
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' intent = Stripe::PaymentIntent.create({ amount: 1099, currency: 'usd', }) client_secret = payment_intent['client_secret'] # Pass the client secret to the client
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' intent = stripe.PaymentIntent.create( amount=1099, currency='usd', ) client_secret = intent.client_secret # Pass the client secret to the client
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); $intent = \Stripe\PaymentIntent::create([ 'amount' => 1099, 'currency' => 'usd', ]); $client_secret = $intent->client_secret; // Pass the client secret to the client
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(1099L) .setCurrency("usd") .build(); PaymentIntent intent = PaymentIntent.create(params); String clientSecret = intent.getClientSecret(); // Pass the client secret to the client
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'usd', }); const clientSecret = paymentIntent.client_secret // Pass the client secret to the client
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1099), Currency: stripe.String(string(stripe.CurrencyUSD)), } pi, _ := paymentintent.New(params) // Pass the client secret to the client
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; var options = new PaymentIntentCreateOptions { Amount = 1099, Currency = "usd", }; var service = new PaymentIntentService(); var paymentIntent = service.Create(options); // Pass the client secret to the client


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.
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 } } }
- (void)paymentContext:(STPPaymentContext *)paymentContext didCreatePaymentResult:(STPPaymentResult *)paymentResult completion:(STPErrorBlock)completion { // Request a PaymentIntent from your backend [[MyAPIClient sharedClient] createPaymentIntentWithCompletion:^(MyAPIClientResult status, NSString * _Nullable paymentIntentClientSecret, NSError * _Nullable error) { if (paymentIntentClientSecret == nil || error != nil) { completion(STPPaymentStatusError, error); // Report error from your API return; } // Assemble the PaymentIntent parameters STPPaymentIntentParams *paymentIntentParams = [[STPPaymentIntentParams alloc] initWithClientSecret: paymentIntentClientSecret]; paymentIntentParams.paymentMethodId = paymentResult.paymentMethod.stripeId; // Confirm the PaymentIntent [[STPPaymentHandler sharedHandler] confirmPayment:paymentIntentParams withAuthenticationContext:paymentContext completion:^(STPPaymentHandlerActionStatus status, STPPaymentIntent * _Nullable paymentIntent, NSError * _Nullable error) { switch (status) { case STPPaymentHandlerActionStatusSucceeded: // Your backend asynchronously fulfills the customer's order, e.g. via webhook completion(STPPaymentStatusSuccess, nil); break; case STPPaymentHandlerActionStatusFailed: completion(STPPaymentStatusError, error); break; case STPPaymentHandlerActionStatusCanceled: completion(STPPaymentStatusUserCancellation, nil); 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:

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 } }
- (void)paymentContext:(STPPaymentContext *)paymentContext didFinishWithStatus:(STPPaymentStatus)status error:(NSError *)error { switch (status) { case STPPaymentStatusSuccess: [self showReceipt]; break; case STPPaymentStatusError: [self showError:error]; break; case STPPaymentStatusUserCancellation: 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.

func paymentContext(_ paymentContext: STPPaymentContext, didFailToLoadWithError error: Error) { self.navigationController?.popViewController(animated: true) // Show the error to your user, etc. }
- (void)paymentContext:(STPPaymentContext *)paymentContext didFailToLoadWithError:(NSError *)error { [self.navigationController popViewControllerAnimated:YES]; // Show the error to your user, etc. }

8 Test the integration

By this point you should have a basic card integration that collects card details and makes a payment.

There are several test cards you can use in test mode to make sure this integration is ready. Use them with any CVC, postal code, and future expiration date.

Number Description
4242424242424242 Succeeds and immediately processes the payment.
4000002500003155 Requires authentication. Stripe will trigger a modal asking for the customer to authenticate.
4000000000009995 Always fails with a decline code of insufficient_funds.

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

Optional Handle post-payment events

Stripe sends a payment_intent.succeeded event when the payment completes. Use the Dashboard, a custom webhook, or a partner solution to receive these events and run actions, like sending an order confirmation email to your customer, logging the sale in a database, or starting a shipping workflow.

Listen for these events rather than waiting on a callback from the client. On the client, the customer could close the browser window or quit the app before the callback executes. Setting up your integration to listen for asynchronous events also makes it easier to accept more payment methods in the future. Check out our guide to payment methods to see the differences between all supported payment methods.

Receive events and run business actions


Use the Stripe Dashboard to view all your Stripe payments, send email receipts, handle payouts, or retry failed payments.

Custom code

Build a webhook handler to listen for events and build custom asynchronous payment flows. Test and debug your webhook integration locally with the Stripe CLI.

Prebuilt apps

Handle common business events, like shipping and inventory management, by integrating a partner application.

Optional Set up Apple Pay Client-side

Register for an Apple Merchant ID

First, you’ll need to obtain an Apple Merchant ID. Start by heading to the Registering a Merchant ID page on the Apple Developer website.

Fill out the form with a description and identifier. Your description is for your own records and can be modified in the future (we recommend just using the name of your app). The identifier must be unique (across all apps, not just yours) and can’t be changed later (although you can always make another one). We recommend using{{your_app_name}}.

After you’ve obtained an Apple Merchant ID, set it in your configuration:

STPPaymentConfiguration.shared().appleMerchantIdentifier = "your apple merchant identifier"
[[STPPaymentConfiguration sharedConfiguration] setAppleMerchantIdentifier:@"your apple merchant identifier"];

Create a new Apple Pay certificate

You need to include a certificate in your app to encrypt outgoing payment data. This involves 3 steps:

  1. Obtain a CSR (certificate signing request) file from Stripe
  2. Use this CSR to generate a certificate through Apple
  3. Upload the certificate back to Stripe

First, head to the Apple Pay Settings page in the Dashboard. Choose Add new application and download the .certSigningRequest file.

Next, back on the Apple Developer site, visit the Add iOS Certificate page. Choose Apple Pay Certificate from the options and click Continue. On the next page, choose the Merchant ID you created earlier from the dropdown and continue.

The next page explains that you can obtain a CSR from your Payment Provider (which at this point you’ve done already) or create one manually. Important note: you must use the CSR provided by Stripe - creating your own won’t work. So ignore the directions at the bottom of this page and continue on.

You’ll be prompted to upload a .certSigningRequest file. Choose the file you downloaded from the Dashboard and continue. You’ll see a success page, with an option to download your certificate. Download it. Finally, return to the Dashboard and upload this .cer file to Stripe.

Integrate with Xcode

Add the Apple Pay capability to your app. In Xcode, open your project settings, choose the Capabilities tab, and enable the Apple Pay switch. You may be prompted to log in to your developer account at this point. Enable the checkbox next to the merchant ID you created earlier, and your app is ready to accept Apple Pay!

Xcode capabilities pane

Enable the Apple Pay capability in Xcode

STPPaymentContext will now display Apple Pay as a payment option in the payment options view controller.

Optional Customize the UI Client-side

The appearance of the UI components is customizable by using the STPTheme object.

Stripe iOS UI Theming

You can override any of the properties from the default theme, typically in your AppDelegate:

STPTheme.default().accentColor =
[[STPTheme defaultTheme] setAccentColor:[UIColor blueColor]];

Here is a list of properties you can customize and their respective usage inside the payment selector UI:

Property Name Description
primaryBackgroundColor Background color for any views in this theme
secondaryBackgroundColor Background color for any supplemental views inside a view (for example the cells of a table view)
primaryForegroundColor Text color for any important labels in a view
secondaryForegroundColor Text color for any supplementary labels in a view
accentColor Color for any buttons and other elements on a view that are important to highlight
errorColor Color for rendering any error messages or views
font Font to be used for all views
emphasisFont Medium-weight font to be used for all bold text in views

You can also customize the fonts in the UI. By default, the SDK will use the system font at different weight and sizes. To override the defaults, set the font and emphasisFont properties and the SDK will automatically use your choice instead and infer the rest.

See also

Was this page helpful?
Questions? Contact us.
Developer tutorials on YouTube.