IOS SDK
Introduction
Payrails iOS SDK provides you with the building blocks to create a seamless checkout experience for your customers. The SDK offers a variety of UI components and payment methods, including card payments, Apple Pay, PayPal, and redirect-based payment methods.
Installation
Minimum Requirements
- iOS 14.0 or later
- Swift 5.0 or later
Using CocoaPods
Add the following line to your Podfile
:
pod 'Payrails/Checkout'
Then run:
pod install
Using Swift Package Manager
- In Xcode, go to File > Add Packages...
- Enter the repository URL:
https://github.com/payrails/ios-sdk
- Select the
PayrailsCheckout
product
The SDK includes dependencies for PayPal's checkout SDK and Payrails' Client-Side Encryption (CSE) library. These are managed automatically by CocoaPods or Swift Package Manager.
Enabling Apple Pay Capability
To use Apple Pay in your app:
- Add the Apple Pay capability in your Xcode project's "Signing & Capabilities" tab for your app target
- Configure a Merchant ID with Apple
SDK Initialization
Before using any of the SDK's features, you need to initialize it by creating a Payrails.Session
object.
Using Async/Await
import Payrails
// Create initialization data with version and base64 data from your backend
let initData = Payrails.InitData(
version: "your_sdk_version_from_backend",
data: "your_base64_data_from_backend"
)
// Configure environment options
let options = Payrails.Options(env: .dev) // Use .prod for production
// Create configuration
let configuration = Payrails.Configuration(
initData: initData,
option: options
)
// Initialize the SDK
do {
let session = try await Payrails.configure(with: configuration)
// Store the session for later use
self.payrailsSession = session
} catch {
// Handle initialization error
print("Payrails SDK initialization failed: \(error)")
}
Using Callback
import Payrails
// Create initialization data with version and base64 data from your backend
let initData = Payrails.InitData(
version: "your_sdk_version_from_backend",
data: "your_base64_data_from_backend"
)
// Configure environment options
let options = Payrails.Options(env: .dev) // Use .prod for production
// Create configuration
let configuration = Payrails.Configuration(
initData: initData,
option: options
)
// Initialize the SDK
Payrails.configure(with: configuration) { [weak self] result in
switch result {
case .success(let session):
// Store the session for later use
self?.payrailsSession = session
case .failure(let error):
// Handle initialization error
print("Payrails SDK initialization failed: \(error)")
}
}
Payment Flow Presenter
Many payment methods require presenting additional UI screens (such as 3D Secure authentication, PayPal login, or redirect pages). The SDK uses the PaymentPresenter
protocol to handle these presentations.
Implementing PaymentPresenter
Your view controller that initiates payments should conform to the PaymentPresenter
protocol:
import Payrails
import UIKit
class CheckoutViewController: UIViewController, PaymentPresenter {
// MARK: - PaymentPresenter Protocol
// This method will be called by the SDK when it needs to present a view controller
// (e.g., for 3DS authentication or PayPal login)
func presentPayment(_ viewController: UIViewController) {
// Present the view controller modally
self.present(viewController, animated: true)
}
// This property is used by the SDK to pass encrypted card data
// You typically don't need to interact with it directly
var encryptedCardData: String?
}
You must assign an object conforming to PaymentPresenter
to the presenter
property of UI payment elements (like CardPaymentButton
, ApplePayButton
, etc.) before initiating a payment.
UI Elements
The SDK provides several UI components for different payment methods. All UI elements are created using static factory methods on the Payrails
class after SDK initialization.
Card Form
The Card Form (Payrails.CardForm
) is a UIView
subclass that provides a secure and customizable form for collecting card payment details.
Creating a Card Form
// Create a card form with default configuration
let cardForm = Payrails.createCardForm()
// Or with custom configuration
let config = CardFormConfig(
showNameField: true,
showSaveInstrument: false,
styles: customStyles, // Optional CardFormStylesConfig
translations: customTranslations // Optional CardTranslations
)
let cardForm = Payrails.createCardForm(config: config, showSaveInstrument: true)
// Add the card form to your view hierarchy
view.addSubview(cardForm)
// Set up constraints...
Key Properties and Methods
delegate: PayrailsCardFormDelegate?
: Assign an object to receive callbacks when card data is collected or an error occurs.saveInstrument: Bool
: Get or set whether the "save card" option is enabled (if the toggle is visible).collectFields()
: Call this method to trigger the collection and encryption of the card data. The result will be delivered via the delegate methods.
Styling the Card Form
The CardFormConfig
object allows you to customize the appearance of the card form:
// Create custom styles for input fields
let cardNumberStyle = CardFieldSpecificStyles(
base: CardStyle(
textColor: .black,
font: UIFont.systemFont(ofSize: 16),
borderColor: .lightGray,
borderWidth: 1,
cornerRadius: 8,
padding: UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
),
focus: CardStyle(borderColor: .blue),
completed: CardStyle(borderColor: .green),
invalid: CardStyle(borderColor: .red)
)
// Create a styles configuration
let stylesConfig = CardFormStylesConfig(
wrapperStyle: CardWrapperStyle(
backgroundColor: .white,
borderColor: .gray,
borderWidth: 1,
cornerRadius: 8,
padding: UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
),
errorTextStyle: CardStyle(textColor: .red, font: UIFont.systemFont(ofSize: 12)),
allInputFieldStyles: CardFieldSpecificStyles.defaultStyle, // Base style for all fields
inputFieldStyles: [.CARD_NUMBER: cardNumberStyle], // Override for specific fields
labelStyles: [.CARD_NUMBER: CardStyle(textColor: .darkGray, font: UIFont.systemFont(ofSize: 14))]
)
Customizing Text and Translations
The CardTranslations
object allows you to customize the text displayed in the card form:
let translations = CardTranslations(
placeholders: CardTranslations.Placeholders(values: [
.CARD_NUMBER: "Card Number",
.CVV: "Security Code",
.EXPIRATION_DATE: "MM/YY",
.CARDHOLDER_NAME: "Name on Card"
]),
labels: CardTranslations.Labels(
values: [
.CARD_NUMBER: "Card Number",
.CVV: "CVV"
],
saveInstrument: "Save this card for future payments"
),
error: CardTranslations.ErrorMessages(values: [
.CARD_NUMBER: "Please enter a valid card number",
.CVV: "Please enter a valid security code",
.EXPIRATION_DATE: "Please enter a valid expiration date"
])
)
Example Usage
class PaymentViewController: UIViewController, PayrailsCardFormDelegate {
var cardForm: Payrails.CardForm?
func setupCardForm() {
// Create the card form
self.cardForm = Payrails.createCardForm(showSaveInstrument: true)
guard let cardForm = self.cardForm else { return }
// Set delegate
cardForm.delegate = self
// Add to view hierarchy
view.addSubview(cardForm)
// Set up constraints
cardForm.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
cardForm.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
cardForm.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
cardForm.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)
])
}
// MARK: - PayrailsCardFormDelegate
func cardForm(_ view: Payrails.CardForm, didCollectCardData data: String) {
print("Card data collected successfully")
// Proceed with payment using the encrypted data
}
func cardForm(_ view: Payrails.CardForm, didFailWithError error: Error) {
print("Failed to collect card data: \(error.localizedDescription)")
// Handle the error
}
}
Card Payment Button
The Card Payment Button (Payrails.CardPaymentButton
) is a button that works with a CardForm
to initiate and manage the card payment process.
Creating a Card Payment Button
// First, create a card form (as shown in the previous section)
let cardForm = Payrails.createCardForm()
// Then create a card payment button
let buttonTranslations = CardPaymenButtonTranslations(label: "Pay Now")
let buttonStyle = CardButtonStyle(
backgroundColor: .systemBlue,
textColor: .white,
font: UIFont.systemFont(ofSize: 16, weight: .semibold),
cornerRadius: 8
)
let payButton = Payrails.createCardPaymentButton(
buttonStyle: buttonStyle,
translations: buttonTranslations
)
// Add the button to your view hierarchy
view.addSubview(payButton)
// Set up constraints...
Key Properties
delegate: PayrailsCardPaymentButtonDelegate?
: Assign an object to receive notifications about button taps and payment outcomes.presenter: PaymentPresenter?
: Required. Assign a view controller that conforms toPaymentPresenter
to handle UI presentations during the payment flow.
Interaction Flow
- User fills in the card form.
- User taps the card payment button.
- The button tells the card form to collect and encrypt the card data.
- If successful, the button automatically initiates the payment process.
- Results are communicated via the
PayrailsCardPaymentButtonDelegate
.
Example Usage
class PaymentViewController: UIViewController, PayrailsCardFormDelegate, PayrailsCardPaymentButtonDelegate, PaymentPresenter {
var cardForm: Payrails.CardForm?
var payButton: Payrails.CardPaymentButton?
func setupPaymentUI() {
// Create card form
self.cardForm = Payrails.createCardForm(showSaveInstrument: true)
// Create payment button
let buttonTranslations = CardPaymenButtonTranslations(label: "Pay Now")
self.payButton = Payrails.createCardPaymentButton(
buttonStyle: nil, // Use default style
translations: buttonTranslations
)
guard let cardForm = self.cardForm, let payButton = self.payButton else { return }
// Set up delegates and presenter
cardForm.delegate = self
payButton.delegate = self
payButton.presenter = self // Self conforms to PaymentPresenter
// Add to view hierarchy
view.addSubview(cardForm)
view.addSubview(payButton)
// Set up constraints...
}
// MARK: - PayrailsCardPaymentButtonDelegate
func onPaymentButtonClicked(_ button: Payrails.CardPaymentButton) {
print("Payment button clicked")
}
func onAuthorizeSuccess(_ button: Payrails.CardPaymentButton) {
print("Payment authorized successfully")
// Navigate to success screen
}
func onAuthorizeFailed(_ button: Payrails.CardPaymentButton) {
print("Payment authorization failed")
// Show error message
}
// MARK: - PaymentPresenter
func presentPayment(_ viewController: UIViewController) {
present(viewController, animated: true)
}
var encryptedCardData: String?
}
Apple Pay Button
The Apple Pay Button (Payrails.ApplePayButton
or Payrails.ApplePayButtonWithToggle
) provides an Apple Pay payment option.
Creating an Apple Pay Button
// Create an Apple Pay button
let applePayButton = Payrails.createApplePayButton(
type: .buy, // PKPaymentButtonType
style: .black, // PKPaymentButtonStyle
showSaveInstrument: false // Set to true to show a "Save instrument" toggle
)
// Add the button to your view hierarchy
view.addSubview(applePayButton)
// Set up constraints...
Key Properties
delegate: PayrailsApplePayButtonDelegate?
: Assign an object to receive notifications about button taps and payment outcomes.presenter: PaymentPresenter?
: Required. Assign a view controller that conforms toPaymentPresenter
.saveInstrument: Bool
: Set totrue
to save the payment instrument after a successful transaction. IfshowSaveInstrument
wastrue
during creation, this property reflects the toggle's state.isEnabled: Bool
: Controls the enabled state of the button.
Apple Pay Button with Toggle
If you pass showSaveInstrument: true
to createApplePayButton()
, it returns a Payrails.ApplePayButtonWithToggle
that includes a switch for the user to choose whether to save their payment details.
Example Usage
class PaymentViewController: UIViewController, PayrailsApplePayButtonDelegate, PaymentPresenter {
var applePayButton: Payrails.ApplePayElement?
func setupApplePayButton() {
// Check if Apple Pay is available
if Payrails.Session.isApplePayAvailable() {
// Create Apple Pay button with toggle
self.applePayButton = Payrails.createApplePayButton(
type: .buy,
style: .black,
showSaveInstrument: true
)
guard let applePayButton = self.applePayButton else { return }
// Set up delegate and presenter
applePayButton.delegate = self
applePayButton.presenter = self
// Add to view hierarchy
view.addSubview(applePayButton)
// Set up constraints...
}
}
// MARK: - PayrailsApplePayButtonDelegate
func onPaymentButtonClicked(_ button: Payrails.ApplePayButton) {
print("Apple Pay button clicked")
}
func onAuthorizeSuccess(_ button: Payrails.ApplePayButton) {
print("Apple Pay payment successful")
// Navigate to success screen
}
func onAuthorizeFailed(_ button: Payrails.ApplePayButton) {
print("Apple Pay payment failed")
// Show error message
}
func onPaymentSessionExpired(_ button: Payrails.ApplePayButton) {
print("Apple Pay session expired or was cancelled by user")
}
// MARK: - PaymentPresenter
func presentPayment(_ viewController: UIViewController) {
present(viewController, animated: true)
}
var encryptedCardData: String?
}
PayPal Button
The PayPal Button (Payrails.PayPalButton
or Payrails.PayPalButtonWithToggle
) provides a PayPal payment option.
Creating a PayPal Button
// Create a PayPal button
let payPalButton = Payrails.createPayPalButton(
showSaveInstrument: false // Set to true to show a "Save instrument" toggle
)
// Add the button to your view hierarchy
view.addSubview(payPalButton)
// Set up constraints...
Key Properties
delegate: PayrailsPayPalButtonDelegate?
: Assign an object to receive notifications about button taps and payment outcomes.presenter: PaymentPresenter?
: Required. Assign a view controller that conforms toPaymentPresenter
to handle the presentation of the PayPal web login.saveInstrument: Bool
: Set totrue
to save the payment instrument after a successful transaction. IfshowSaveInstrument
wastrue
during creation, this property reflects the toggle's state.isEnabled: Bool
: Controls the enabled state of the button.
Customizing the PayPal Button
You can set a prefix text for the PayPal button:
if let payPalButton = payPalButton as? Payrails.PayPalButton {
payPalButton.setTitle("Pay with", for: .normal)
}
PayPal Button with Toggle
If you pass showSaveInstrument: true
to createPayPalButton()
, it returns a Payrails.PayPalButtonWithToggle
that includes a switch for the user to choose whether to save their payment details.
Example Usage
class PaymentViewController: UIViewController, PayrailsPayPalButtonDelegate, PaymentPresenter {
var payPalButton: Payrails.PaypalElement?
func setupPayPalButton() {
// Create PayPal button with toggle
self.payPalButton = Payrails.createPayPalButton(showSaveInstrument: true)
guard let payPalButton = self.payPalButton else { return }
// Set up delegate and presenter
payPalButton.delegate = self
payPalButton.presenter = self
// Add to view hierarchy
view.addSubview(payPalButton)
// Set up constraints...
}
// MARK: - PayrailsPayPalButtonDelegate
func onPaymentButtonClicked(_ button: Payrails.PayPalButton) {
print("PayPal button clicked")
}
func onAuthorizeSuccess(_ button: Payrails.PayPalButton) {
print("PayPal payment successful")
// Navigate to success screen
}
func onAuthorizeFailed(_ button: Payrails.PayPalButton) {
print("PayPal payment failed")
// Show error message
}
func onPaymentSessionExpired(_ button: Payrails.PayPalButton) {
print("PayPal session expired or was cancelled by user")
}
// MARK: - PaymentPresenter
func presentPayment(_ viewController: UIViewController) {
present(viewController, animated: true)
}
var encryptedCardData: String?
}
Generic Redirect Button
The Generic Redirect Button (Payrails.GenericRedirectButton
) is used for payment methods that require redirecting the user to an external webpage (e.g., Klarna, Sofort, iDEAL).
Creating a Generic Redirect Button
// Create a button for a specific payment method (e.g., iDEAL)
let buttonTranslations = CardPaymenButtonTranslations(label: "Pay with iDEAL")
let buttonStyle = CardButtonStyle(
backgroundColor: .systemBlue,
textColor: .white,
cornerRadius: 8
)
let idealButton = Payrails.createGenericRedirectButton(
buttonStyle: buttonStyle,
translations: buttonTranslations,
paymentMethodCode: "ideal" // The code for the specific payment method
)
// Add the button to your view hierarchy
view.addSubview(idealButton)
// Set up constraints...
Key Properties
delegate: GenericRedirectPaymentButtonDelegate?
: Assign an object to receive notifications about button taps and payment outcomes.presenter: PaymentPresenter?
: Required. Assign a view controller that conforms toPaymentPresenter
to handle the presentation of the redirect web view.
Example Usage
class PaymentViewController: UIViewController, GenericRedirectPaymentButtonDelegate, PaymentPresenter {
var idealButton: Payrails.GenericRedirectButton?
func setupIdealButton() {
// Create iDEAL button
let buttonTranslations = CardPaymenButtonTranslations(label: "Pay with iDEAL")
self.idealButton = Payrails.createGenericRedirectButton(
buttonStyle: nil, // Use default style
translations: buttonTranslations,
paymentMethodCode: "ideal"
)
guard let idealButton = self.idealButton else { return }
// Set up delegate and presenter
idealButton.delegate = self
idealButton.presenter = self
// Add to view hierarchy
view.addSubview(idealButton)
// Set up constraints...
}
// MARK: - GenericRedirectPaymentButtonDelegate
func onPaymentButtonClicked(_ button: Payrails.GenericRedirectButton) {
print("iDEAL button clicked")
}
func onAuthorizeSuccess(_ button: Payrails.GenericRedirectButton) {
print("iDEAL payment successful")
// Navigate to success screen
}
func onAuthorizeFailed(_ button: Payrails.GenericRedirectButton) {
print("iDEAL payment failed")
// Show error message
}
func onPaymentSessionExpired(_ button: Payrails.GenericRedirectButton) {
print("iDEAL session expired or was cancelled by user")
}
// MARK: - PaymentPresenter
func presentPayment(_ viewController: UIViewController) {
present(viewController, animated: true)
}
var encryptedCardData: String?
}
Stored Instruments
The Payrails SDK allows you to securely store and manage customer payment instruments (like cards and PayPal accounts) for faster checkouts. The SDK provides UI components to display these stored instruments and handle payments.
Retrieving Stored Instruments
Before displaying stored instruments, you need to fetch them. You can retrieve all stored instruments or filter by type (card or PayPal).
Using the Payrails Session (recommended for UI components):
After initializing the Payrails.Session
, you can access stored instruments:
guard let payrailsSession = self.payrailsSession else { return }
// Get all card instruments
let cardInstruments = payrailsSession.storedInstruments(for: .card)
// Get all PayPal instruments
let payPalInstruments = payrailsSession.storedInstruments(for: .payPal)
// Get all instruments (both card and PayPal)
let allInstruments = cardInstruments + payPalInstruments
Using Static Methods (e.g., for non-UI logic or if session is managed elsewhere):
// Get all stored instruments
let allInstruments = Payrails.getStoredInstruments()
// Get stored instruments of a specific type
let cardInstruments = Payrails.getStoredInstruments(for: .card)
let payPalInstruments = Payrails.getStoredInstruments(for: .payPal)
Each instrument conforms to the StoredInstrument
protocol:
public protocol StoredInstrument {
var id: String { get } // Unique identifier for the instrument
var email: String? { get } // Email (for PayPal)
var description: String? { get } // Masked card number or PayPal display name
var type: Payrails.PaymentType { get } // .card or .payPal
}
Displaying a List of Stored Instruments (Payrails.StoredInstruments
)
Payrails.StoredInstruments
)The Payrails.StoredInstruments
component displays a list of available stored payment methods. It features an accordion-style interface where users can tap an instrument to reveal its payment button.
Creating a StoredInstruments View
// Create with default style and translations
let storedInstrumentsView = Payrails.createStoredInstruments()
// Add to your view hierarchy
view.addSubview(storedInstrumentsView)
// Set up constraints...
// Assign delegate and presenter
storedInstrumentsView.delegate = self // Conforms to PayrailsStoredInstrumentsDelegate
storedInstrumentsView.presenter = self // Conforms to PaymentPresenter
You can also create it with options to show delete and pay buttons directly in the list items:
let storedInstrumentsView = Payrails.createStoredInstruments(
showDeleteButton: true, // Shows a delete icon for each instrument
showPayButton: true // Shows a "Pay" button directly for each instrument (alternative to accordion)
)
Key Properties and Methods
delegate: PayrailsStoredInstrumentsDelegate?
: Handles events like instrument selection, payment success/failure, and delete requests.presenter: PaymentPresenter?
: Required for payment processing.refreshInstruments()
: Call this to reload and update the list of instruments (e.g., after a new instrument is saved or one is deleted).
Customization
You can customize the appearance and text using StoredInstrumentsStyle
and StoredInstrumentsTranslations
.
Styling (StoredInstrumentsStyle
):
let customStyle = StoredInstrumentsStyle(
backgroundColor: .systemGroupedBackground,
itemBackgroundColor: .secondarySystemGroupedBackground,
selectedItemBackgroundColor: .systemBlue.withAlphaComponent(0.2),
labelTextColor: .label,
labelFont: .systemFont(ofSize: 17, weight: .medium),
itemCornerRadius: 10,
itemSpacing: 10,
itemPadding: UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15),
buttonStyle: StoredInstrumentButtonStyle( // Style for the "Pay" button
backgroundColor: .systemGreen,
textColor: .white,
font: .systemFont(ofSize: 16, weight: .semibold),
cornerRadius: 8,
height: 44
),
deleteButtonStyle: DeleteButtonStyle( // Style for the delete icon/button
backgroundColor: .clear,
textColor: .systemRed,
font: .systemFont(ofSize: 20), // For SF Symbols or text
size: CGSize(width: 30, height: 30)
)
)
Translations (StoredInstrumentsTranslations
):
let customTranslations = StoredInstrumentsTranslations(
cardPrefix: "Use Card:",
paypalPrefix: "Use PayPal:",
buttonTranslations: StoredInstrumentButtonTranslations(
label: "Pay with this method",
processingLabel: "Authorizing..."
)
)
// Create the component with custom style and translations
let styledStoredInstrumentsView = Payrails.createStoredInstruments(
style: customStyle,
translations: customTranslations,
showDeleteButton: true
)
Delegate (PayrailsStoredInstrumentsDelegate
)
PayrailsStoredInstrumentsDelegate
)Implement this delegate to respond to user interactions:
protocol PayrailsStoredInstrumentsDelegate: AnyObject {
// Called when a user taps on an instrument in the list.
func storedInstruments(_ view: Payrails.StoredInstruments, didSelectInstrument instrument: StoredInstrument)
// Called when a payment initiated from this component succeeds.
func storedInstruments(_ view: Payrails.StoredInstruments, didCompletePaymentForInstrument instrument: StoredInstrument)
// Called when a payment initiated from this component fails.
func storedInstruments(_ view: Payrails.StoredInstruments, didFailPaymentForInstrument instrument: StoredInstrument, error: PayrailsError)
// Called when the user taps the delete button for an instrument (if showDeleteButton is true).
// You are responsible for confirming and calling Payrails.deleteInstrument().
func storedInstruments(_ view: Payrails.StoredInstruments, didRequestDeleteInstrument instrument: StoredInstrument)
}
Displaying a Single Stored Instrument (Payrails.StoredInstrumentView
)
Payrails.StoredInstrumentView
)If you want to display individual stored instruments outside of the StoredInstruments
list component (e.g., in a custom layout), you can use Payrails.StoredInstrumentView
.
Creating a StoredInstrumentView
// Assume 'myStoredCard' is a StoredInstrument object you've retrieved
guard let firstCard = Payrails.getStoredInstruments(for: .card).first else { return }
let singleInstrumentView = Payrails.createStoredInstrumentView(
instrument: firstCard,
showDeleteButton: true,
showPayButton: true // Shows a payment button for this specific instrument
)
// Add to your view hierarchy
view.addSubview(singleInstrumentView)
// Set up constraints...
// Assign delegate and presenter
singleInstrumentView.delegate = self // Conforms to PayrailsStoredInstrumentViewDelegate
singleInstrumentView.setPresenter(self) // Conforms to PaymentPresenter
Customization
StoredInstrumentView
uses the same StoredInstrumentsStyle
and StoredInstrumentsTranslations
objects for consistency.
let customStyle = StoredInstrumentsStyle(...) // Define as above
let customTranslations = StoredInstrumentsTranslations(...) // Define as above
let styledSingleInstrumentView = Payrails.createStoredInstrumentView(
instrument: firstCard,
style: customStyle,
translations: customTranslations,
showDeleteButton: true,
showPayButton: true
)
Delegate (PayrailsStoredInstrumentViewDelegate
)
PayrailsStoredInstrumentViewDelegate
)This delegate is specific to the single instrument view:
public protocol PayrailsStoredInstrumentViewDelegate: AnyObject {
// Called when the instrument view is tapped and selected.
func storedInstrumentView(_ view: Payrails.StoredInstrumentView, didSelectInstrument instrument: StoredInstrument)
// Called when the instrument view is tapped again and deselected.
func storedInstrumentView(_ view: Payrails.StoredInstrumentView, didDeselectInstrument instrument: StoredInstrument)
// Called on successful payment.
func storedInstrumentView(_ view: Payrails.StoredInstrumentView, didCompletePaymentForInstrument instrument: StoredInstrument)
// Called on payment failure.
func storedInstrumentView(_ view: Payrails.StoredInstrumentView, didFailPaymentForInstrument instrument: StoredInstrument, error: PayrailsError)
// Called when the delete button (if shown) is tapped.
func storedInstrumentView(_ view: Payrails.StoredInstrumentView, didRequestDeleteInstrument instrument: StoredInstrument)
}
Deleting a Stored Instrument
The SDK provides a method to delete stored instruments. This is typically called after the user confirms the deletion (e.g., in response to didRequestDeleteInstrument
delegate calls).
func deleteInstrument(instrumentId: String) async {
do {
let response = try await Payrails.deleteInstrument(instrumentId: instrumentId)
if response.success {
print("Instrument \(instrumentId) deleted successfully.")
// Refresh your UI (e.g., call storedInstrumentsView.refreshInstruments())
} else {
print("Failed to delete instrument \(instrumentId).")
// Handle server-side deletion failure
}
} catch {
print("Error deleting instrument \(instrumentId): \(error.localizedDescription)")
// Handle error (e.g., network issue)
}
}
// Example usage within a delegate method:
func storedInstruments(_ view: Payrails.StoredInstruments, didRequestDeleteInstrument instrument: StoredInstrument) {
// Present an alert to confirm deletion
let alert = UIAlertController(title: "Delete Payment Method", message: "Are you sure?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
alert.addAction(UIAlertAction(title: "Delete", style: .destructive) { _ in
Task {
await self.deleteInstrument(instrumentId: instrument.id)
// After deletion, refresh the StoredInstruments view
self.storedInstrumentsView?.refreshInstruments()
}
})
self.present(alert, animated: true)
}
Example: Using StoredInstruments Component
import UIKit
import Payrails
class CheckoutViewController: UIViewController, PayrailsStoredInstrumentsDelegate, PaymentPresenter {
var payrailsSession: Payrails.Session? // Assume this is initialized
var storedInstrumentsComponent: Payrails.StoredInstruments?
override func viewDidLoad() {
super.viewDidLoad()
// Assuming payrailsSession is already initialized
setupStoredInstrumentsUI()
}
func setupStoredInstrumentsUI() {
guard self.payrailsSession != nil else {
print("Payrails session not initialized.")
return
}
let customStyle = StoredInstrumentsStyle(itemBackgroundColor: .lightGray)
let customTranslations = StoredInstrumentsTranslations(cardPrefix: "Saved Card:")
storedInstrumentsComponent = Payrails.createStoredInstruments(
style: customStyle,
translations: customTranslations,
showDeleteButton: true, // Allow users to delete instruments
showPayButton: false // Use accordion style for payment
)
guard let storedInstrumentsComponent = storedInstrumentsComponent else { return }
storedInstrumentsComponent.delegate = self
storedInstrumentsComponent.presenter = self // Self conforms to PaymentPresenter
view.addSubview(storedInstrumentsComponent)
storedInstrumentsComponent.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
storedInstrumentsComponent.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
storedInstrumentsComponent.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
storedInstrumentsComponent.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16)
])
}
// MARK: - PayrailsStoredInstrumentsDelegate
func storedInstruments(_ view: Payrails.StoredInstruments, didSelectInstrument instrument: StoredInstrument) {
print("Selected instrument: \(instrument.description ?? instrument.id)")
}
func storedInstruments(_ view: Payrails.StoredInstruments, didCompletePaymentForInstrument instrument: StoredInstrument) {
print("Payment successful with: \(instrument.description ?? instrument.id)")
// Navigate to success screen
}
func storedInstruments(_ view: Payrails.StoredInstruments, didFailPaymentForInstrument instrument: StoredInstrument, error: PayrailsError) {
print("Payment failed for \(instrument.description ?? instrument.id): \(error.localizedDescription)")
// Show error message
}
func storedInstruments(_ view: Payrails.StoredInstruments, didRequestDeleteInstrument instrument: StoredInstrument) {
let alert = UIAlertController(
title: "Confirm Deletion",
message: "Delete \(instrument.description ?? "this payment method")?",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
alert.addAction(UIAlertAction(title: "Delete", style: .destructive) { _ in
Task {
await self.deleteInstrument(instrumentId: instrument.id)
}
})
present(alert, animated: true)
}
private func deleteInstrument(instrumentId: String) async {
do {
let response = try await Payrails.deleteInstrument(instrumentId: instrumentId)
if response.success {
print("Instrument deleted.")
self.storedInstrumentsComponent?.refreshInstruments() // Refresh the list
} else {
print("Deletion failed on server.")
}
} catch {
print("Deletion error: \(error)")
}
}
// MARK: - PaymentPresenter
func presentPayment(_ viewController: UIViewController) {
present(viewController, animated: true)
}
var encryptedCardData: String? // Not used for stored instruments flow directly
}
Event Handling with UI Component Delegates
The Payrails SDK uses delegate patterns to notify your app about events related to UI components. Each UI component has its own delegate protocol.
Card Form Events (PayrailsCardFormDelegate)
The PayrailsCardFormDelegate
protocol is used to receive notifications about card data collection:
protocol PayrailsCardFormDelegate: AnyObject {
func cardForm(_ view: Payrails.CardForm, didCollectCardData data: String)
func cardForm(_ view: Payrails.CardForm, didFailWithError error: Error)
}
cardForm(_:didCollectCardData:)
: Called when card data is successfully collected and encrypted. Thedata
parameter contains the encrypted card data.cardForm(_:didFailWithError:)
: Called if an error occurs during collection or encryption.
Card Payment Button Events (PayrailsCardPaymentButtonDelegate)
The PayrailsCardPaymentButtonDelegate
protocol is used to receive notifications about card payment button actions:
protocol PayrailsCardPaymentButtonDelegate: AnyObject {
func onPaymentButtonClicked(_ button: Payrails.CardPaymentButton)
func onAuthorizeSuccess(_ button: Payrails.CardPaymentButton)
func onAuthorizeFailed(_ button: Payrails.CardPaymentButton)
}
onPaymentButtonClicked(_:)
: Called when the button is tapped, before card collection starts.onAuthorizeSuccess(_:)
: Called when the payment authorization is successful.onAuthorizeFailed(_:)
: Called if payment authorization fails for any reason.
Apple Pay Button Events (PayrailsApplePayButtonDelegate)
The PayrailsApplePayButtonDelegate
protocol is used to receive notifications about Apple Pay button actions:
protocol PayrailsApplePayButtonDelegate: AnyObject {
func onPaymentButtonClicked(_ button: Payrails.ApplePayButton)
func onAuthorizeSuccess(_ button: Payrails.ApplePayButton)
func onAuthorizeFailed(_ button: Payrails.ApplePayButton)
func onPaymentSessionExpired(_ button: Payrails.ApplePayButton)
}
onPaymentButtonClicked(_:)
: Called when the button is tapped.onAuthorizeSuccess(_:)
: Called when the payment authorization is successful.onAuthorizeFailed(_:)
: Called if payment authorization fails.onPaymentSessionExpired(_:)
: Called if the Apple Pay sheet is cancelled by the user.
PayPal Button Events (PayrailsPayPalButtonDelegate)
The PayrailsPayPalButtonDelegate
protocol is used to receive notifications about PayPal button actions:
protocol PayrailsPayPalButtonDelegate: AnyObject {
func onPaymentButtonClicked(_ button: Payrails.PayPalButton)
func onAuthorizeSuccess(_ button: Payrails.PayPalButton)
func onAuthorizeFailed(_ button: Payrails.PayPalButton)
func onPaymentSessionExpired(_ button: Payrails.PayPalButton)
}
onPaymentButtonClicked(_:)
: Called when the button is tapped.onAuthorizeSuccess(_:)
: Called when the payment authorization is successful.onAuthorizeFailed(_:)
: Called if payment authorization fails.onPaymentSessionExpired(_:)
: Called if the PayPal flow is cancelled by the user.
Generic Redirect Button Events (GenericRedirectPaymentButtonDelegate)
The GenericRedirectPaymentButtonDelegate
protocol is used to receive notifications about redirect-based payment button actions:
protocol GenericRedirectPaymentButtonDelegate: AnyObject {
func onPaymentButtonClicked(_ button: Payrails.GenericRedirectButton)
func onAuthorizeSuccess(_ button: Payrails.GenericRedirectButton)
func onAuthorizeFailed(_ button: Payrails.GenericRedirectButton)
func onPaymentSessionExpired(_ button: Payrails.GenericRedirectButton)
}
onPaymentButtonClicked(_:)
: Called when the button is tapped.onAuthorizeSuccess(_:)
: Called when the payment authorization is successful.onAuthorizeFailed(_:)
: Called if payment authorization fails.onPaymentSessionExpired(_:)
: Called if the redirect flow is cancelled by the user.
Example: Implementing Multiple Delegates
A single view controller can implement multiple delegate protocols to handle different payment methods:
class CheckoutViewController: UIViewController,
PayrailsCardFormDelegate,
PayrailsCardPaymentButtonDelegate,
PayrailsApplePayButtonDelegate,
PayrailsPayPalButtonDelegate,
GenericRedirectPaymentButtonDelegate,
PaymentPresenter {
// UI components
var cardForm: Payrails.CardForm?
var cardPayButton: Payrails.CardPaymentButton?
var applePayButton: Payrails.ApplePayElement?
var payPalButton: Payrails.PaypalElement?
var idealButton: Payrails.GenericRedirectButton?
// MARK: - Setup Methods
func setupPaymentMethods() {
setupCardPayment()
setupApplePay()
setupPayPal()
setupIdeal()
}
// Setup methods for each payment method...
// MARK: - PayrailsCardFormDelegate
func cardForm(_ view: Payrails.CardForm, didCollectCardData data: String) {
print("Card data collected successfully")
}
func cardForm(_ view: Payrails.CardForm, didFailWithError error: Error) {
print("Failed to collect card data: \(error.localizedDescription)")
}
// MARK: - PayrailsCardPaymentButtonDelegate
func onPaymentButtonClicked(_ button: Payrails.CardPaymentButton) {
print("Card payment button clicked")
}
func onAuthorizeSuccess(_ button: Payrails.CardPaymentButton) {
print("Card payment successful")
showSuccessScreen()
}
func onAuthorizeFailed(_ button: Payrails.CardPaymentButton) {
print("Card payment failed")
showErrorMessage("Payment failed")
}
// MARK: - PayrailsApplePayButtonDelegate
func onPaymentButtonClicked(_ button: Payrails.ApplePayButton) {
print("Apple Pay button clicked")
}
func onAuthorizeSuccess(_ button: Payrails.ApplePayButton) {
print("Apple Pay payment successful")
showSuccessScreen()
}
func onAuthorizeFailed(_ button: Payrails.ApplePayButton) {
print("Apple Pay payment failed")
showErrorMessage("Payment failed")
}
func onPaymentSessionExpired(_ button: Payrails.ApplePayButton) {
print("Apple Pay session expired or was cancelled")
}
// MARK: - PayrailsPayPalButtonDelegate
func onPaymentButtonClicked(_ button: Payrails.PayPalButton) {
print("PayPal button clicked")
}
func onAuthorizeSuccess(_ button: Payrails.PayPalButton) {
print("PayPal payment successful")
showSuccessScreen()
}
func onAuthorizeFailed(_ button: Payrails.PayPalButton) {
print("PayPal payment failed")
showErrorMessage("Payment failed")
}
func onPaymentSessionExpired(_ button: Payrails.PayPalButton) {
print("PayPal session expired or was cancelled")
}
// MARK: - GenericRedirectPaymentButtonDelegate
func onPaymentButtonClicked(_ button: Payrails.GenericRedirectButton) {
print("Redirect payment button clicked")
}
func onAuthorizeSuccess(_ button: Payrails.GenericRedirectButton) {
print("Redirect payment successful")
showSuccessScreen()
}
func onAuthorizeFailed(_ button: Payrails.GenericRedirectButton) {
print("Redirect payment failed")
showErrorMessage("Payment failed")
}
func onPaymentSessionExpired(_ button: Payrails.GenericRedirectButton) {
print("Redirect session expired or was cancelled")
}
// MARK: - PaymentPresenter
func presentPayment(_ viewController: UIViewController) {
present(viewController, animated: true)
}
var encryptedCardData: String?
// MARK: - Helper Methods
private func showSuccessScreen() {
// Navigate to success screen
}
private func showErrorMessage(_ message: String) {
// Show error alert to user
let alert = UIAlertController(title: "Payment Error", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}
Updated 3 days ago