Processing payments using Stripe SDK iOS
This guide explains how to install and set up Stripe SDK on your iOS application.
Aim
Open the inbuilt Address and Payment Sheet of Stripe and start accepting payments from your customer.
Minimum iOS Deployment Version
12.0
Stripe Frameworks
Stripe (22.8.4)
StripeApplePay (22.8.4)
StripeCore (22.8.4)
StripeUICore (22.8.4)
Requirements
MacBook / MacOS
CocoaPods installed on your system
Xcode
Terminal
Proof of Concept
iOS project and test backend for processing payments with Stripe SDK.
Setup
Adding the Stripe dependency
Open Terminal in the project directory and do
pod init
.Open the newly created
Podfile
and set the iOS version to 12.0.# Uncomment the next line to define a global platform for your project platform :ios, '12.0'
Add the Stripe dependency to the
Podfile
.target 'payments with stripe' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! # Pods for payments with stripe pod 'Stripe' end
Save the
Podfile
and do apod install
on the Terminal. The following dependencies showed be installed.pod install
Analyzing dependencies
Downloading dependencies
Installing Stripe (22.8.4)
Installing StripeApplePay (22.8.4)
Installing StripeCore (22.8.4)
Installing StripeUICore (22.8.4)
Generating Pods project
Integrating client projectClose the
payments with stripe.xcodeproj
and openpayments with stripe.xcworkspace
.Select a simulator and run the project. The project should run successfully with a blank screen.
Getting started with Stripe Developers Dashboard
This process should be done once. All the teams will use the same Dashboard. (Backend, iOS, Android etc.)
Go to stripe.com and click on start now.
Follow the steps as directed by the website to complete the registration process.
Complete the email verification and set up your business details for which payments will be accepted by Stripe.
On the Stripe Dashboard, you should be able to see your Publishable key and Secret key for both test mode and live mode.
The publishable key is used on the client side such as in iOS, android or web apps.
The secret key is used on the server side. (Secret key should not be stored on client-facing apps.)
Setup Stripe in your iOS App
Open
AppDelegate.swift
and add the Publishable key inside thedidFinishLaunchingWithOptions
method. The publishable key can be retrieved from the Stripe Developers Dashboard.StripeAPI.defaultPublishableKey = "Insert your key here"
Use the test mode keys during the testing and development phase, and the live mode keys when publishing the app.
We need to hit a POST API to create the payment intent and in return receive the following data. This data is used to initialize the
customer
, Stripe SDK and thePaymentSheet
.customerId
ephemeralKey
paymentIntent
publishableKey
Refer to CourseCheckoutViewModel
in the Proof of Concept example on GitHub.
// Creates a payment intent for the customer and fetches the important keys and id's for processing a payment
func fetchPaymentIntent(completion: @escaping ((Bool, String?) -> Void)) {
// This could be the details for the item that the customer wants to buy
let cartContent: [String: Any] = ["items": [["id": UUID().uuidString]]]
self.apiClient.createPaymentIntent(cartContent: cartContent) { [weak self] (data, error) in
guard let self = self else { return }
guard error == nil else {
print(error.debugDescription)
completion(false, error.debugDescription)
return
}
guard
let customerId = data?.customerId as? String,
let customerEphemeralKeySecret = data?.ephemeralKey as? String,
let paymentIntentClientSecret = data?.paymentIntent as? String,
let publishableKey = data?.publishableKey as? String
else {
let error = "Error fetching required data"
print(error)
completion(false, error)
return
}
print("Created Payment Intent")
self.setPublishableKey(publishableKey: publishableKey)
self.paymentIntentClientSecret = paymentIntentClientSecret
self.customer = .init(id: customerId,
ephemeralKeySecret: customerEphemeralKeySecret)
completion(true, nil)
}
}
- Setup the
PaymentSheet
in your View Controller. Refer toCourseCheckoutViewController
in the Proof of Concept example on GitHub.
// Create and configure the payment sheet
private func createPaymentSheet() {
var configuration = PaymentSheet.Configuration()
// Autofills the shipping details with the one we collected from user through AddressViewController
configuration.shippingDetails = { [weak self] in
return self?.address
}
// Configure button for Apple Pay in PaymentSheet
configuration.applePay = .init(merchantId: "com.example.appname",
merchantCountryCode: "US")
// Provides checkbox for saving card details
configuration.savePaymentMethodOptInBehavior = .requiresOptIn
// Sets the return url. Used when user needs to go outside the app for authentication
configuration.returnURL = "payments-with-stripe://stripe-redirect"
// Configures the color of the pay button
configuration.primaryButtonColor = .blue
// Shows the name of the company collecting the payment
configuration.merchantDisplayName = "Dummy Corp Inc"
// Allows payments which requires some time to confirm
configuration.allowsDelayedPaymentMethods = true
// Adds default billing details for user to the payment object
configuration.defaultBillingDetails.email = "dummy@corp.com"
configuration.defaultBillingDetails.name = self.address?.name
configuration.defaultBillingDetails.phone = self.address?.phone
configuration.defaultBillingDetails.address.country = self.address?.address.country
configuration.defaultBillingDetails.address.city = self.address?.address.city
configuration.defaultBillingDetails.address.line1 = self.address?.address.line1
configuration.defaultBillingDetails.address.line2 = self.address?.address.line2
configuration.defaultBillingDetails.address.state = self.address?.address.state
configuration.defaultBillingDetails.address.postalCode = self.address?.address.postalCode
// Sets customer object to payment object
if let customer = self.viewModel.getCustomer() {
configuration.customer = customer
}
// Initiaises the PaymentSheet with the above config and client secret received from payment intent API
if let paymentIntentClientSecret = self.viewModel.getPaymentIntentClientSecret() {
self.paymentSheet = PaymentSheet(paymentIntentClientSecret: paymentIntentClientSecret,
configuration: configuration)
}
}
- Handle the conditions after the payment. The following three scenarios can happen during/after the payment.
// Present user with the payment sheet and handles the various payment scenarios
private func presentPaymentSheet() {
guard let paymentSheet = paymentSheet else { return }
DispatchQueue.main.async {
paymentSheet.present(from: self) { [weak self] (paymentResult) in
guard let self = self else { return }
switch paymentResult {
case .completed:
self.displayAlert(title: "Payment complete!")
case .canceled:
self.displayAlert(title: "Payment canceled!")
case .failed(let error):
self.displayAlert(title: "Payment failed", message: error.localizedDescription)
}
}
}
}
On tap of the pay now button, present the payment sheet, add the payment details and click on pay.
Either the payment will be completed or it will fail if there are issues with the payment method. We can debug the failure reasons in the following link.
Payment can also result in cancellation if the user dismisses the
PaymentSheet
.
Address collection
We can set up the
AddressViewController
to display a form for collecting names, addresses, cities, countries, postal codes and phone numbers.// Setup and display the address sheet private func presentAddressCollectionController(_ controller: CourseCheckoutViewController) { let addressConfiguration = AddressViewController.Configuration(additionalFields: .init(name: .required, phone: .required), allowedCountries: [], title: "Billing Address") let addressViewController = AddressViewController(configuration: addressConfiguration, delegate: controller) let navigationController = UINavigationController(rootViewController: addressViewController) self.rootViewController.present(navigationController, animated: true) }
Set up the delegate method to get the address details filled in by the customer.
extension CourseCheckoutViewController: AddressViewControllerDelegate { func addressViewControllerDidFinish(_ addressViewController: Stripe.AddressViewController, with address: Stripe.AddressViewController.AddressDetails?) { addressViewController.dismiss(animated: true) { debugPrint(address as Any) self.address = address } } }
The address details collected from the user should be passed to
PaymentSheet
Configuration
. This will auto-fill the details in thePaymentSheet
and also send the customer details to Stripe Dashboard.Collecting these details is essential if the Stripe account is registered in India but the users are paying in international currency. More on this here.
var configuration = PaymentSheet.Configuration() configuration.shippingDetails = { [weak self] in return self?.address }
Enable card scanning
To enable the option for scanning debit/credit cards, add the following key to the info.plist
file.
<key>NSCameraUsageDescription</key>
<string>Allow the app to scan cards.</string>
This feature is available on iOS 13.0 and above.
Process payments using Apple Pay
Steps for integrating Apple Pay are well documented in the links below.
Please check out this tutorial for adding Apple Pay as a payment option to your app.
Add an Apple pay button on your PaymentSheet
by editing the configuration as follows.
var configuration = PaymentSheet.Configuration()
configuration.applePay = .init(merchantId: "merchant.com.your_app_name",
merchantCountryCode: "US")
Return to the app automatically after external authentication
A customer may go out of your app for authentication purposes, for example, in Safari or their banking app to complete payment.
To allow them to automatically return to your app after authenticating, configure a custom URL scheme or universal link and set up your app delegate/scene delegate
(If using scene delegate for iOS 13 and above) to forward the URL to the SDK.
AppDelegate.swift
// This method handles opening custom URL schemes // (for example, "your-app://stripe-redirect") func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:] ) -> Bool { let stripeHandled = StripeAPI.handleURLCallback(with: url) if !stripeHandled { /* This was not a stripe url, do whatever url handling your app normally does, if any. */ } return true } // This method handles opening universal link URLs // (for example, "https://example.com/stripe_ios_callback") func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void ) -> Bool { if userActivity.activityType == NSUserActivityTypeBrowsingWeb { if let url = userActivity.webpageURL { let stripeHandled = StripeAPI.handleURLCallback(with: url) guard !stripeHandled else { return true } /* This was not a stripe url, do whatever url handling your app normally does, if any. */ } } return false }
SceneDelegate.swift
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { if let urlContext = URLContexts.first { let url = urlContext.url let stripeHandled = StripeAPI.handleURLCallback(with: url) if !stripeHandled { // This was not a stripe url, do whatever url handling your app // normally does, if any. } } }
Also, a returnURL
should be configured in the PaymentSheet.Configuration
object to the URL for your app.
var configuration = PaymentSheet.Configuration()
configuration.returnURL = "payments-with-stripe://stripe-redirect"
Clear cookies on logout
PaymentSheet
related cookies and other authentication data should be cleared when a user logs out of the app. Add the following code to your logout function.
import Stripe
// Resets all payment and user related cache
// Should be called when user logs out of the app
func logout() {
/*
Perform all other logout related steps here
*/
PaymentSheet.resetCustomer()
}
FAQ
Stripe SDK can be installed using CocoaPods, Carthage, and Swift Package Manager.
The latest version v23.3.2 or above of Stripe iOS SDK requires Xcode 13.2.1 or later and is compatible with apps targeting iOS 13 or above.
For iOS 12 support use → v22.8.4.
For iOS 11 support use → v21.13.0.
For iOS 10 support use → v19.4.0.
For iOS 9 support use → v17.0.2.
Why was my card declined when trying to make a payment? Check the reason for your decline code.
Got an error message while processing your payment? Check the reason in this article.
Resources
Adding additional articles on security when processing payments using Stripe: