Using iOS Custom UI Components

    Learn about the individual UI components of the Stripe iOS SDK.

    Accepting payments in your app involves 3 steps:

    1. Collecting your user's payment details
    2. Converting the credit card information to a Stripe single-use token
    3. Sending this token to your server to create a charge

    The Stripe iOS SDK has lots of tools to help you with steps 1 and 2. For step 3, we recommend using a networking library such as AFNetworking or Alamofire to talk to your own API.

    STPAddCardViewController

    The STPAddCardViewController class automatically handles both collecting and tokenizing your user's payment information. You can present it modally or push it onto a UINavigationController stack. You can also fully customize its appearance via the theme property.

    func handleAddPaymentOptionButtonTapped() {
        // Setup add card view controller
        let addCardViewController = STPAddCardViewController()
        addCardViewController.delegate = self
    
        // Present add card view controller
        let navigationController = UINavigationController(rootViewController: addCardViewController)
        present(navigationController, animated: true)
    }
    
    // MARK: STPAddCardViewControllerDelegate
    
    func addCardViewControllerDidCancel(_ addCardViewController: STPAddCardViewController) {
        // Dismiss add card view controller
        dismiss(animated: true)
    }
    
    func addCardViewController(_ addCardViewController: STPAddCardViewController, didCreateToken token: STPToken, completion: @escaping STPErrorBlock) {
        submitTokenToBackend(token, completion: { (error: Error?) in
            if let error = error {
                // Show error in add card view controller
                completion(error)
            }
            else {
                // Notify add card view controller that token creation was handled successfully
                completion(nil)
    
                // Dismiss add card view controller
                dismiss(animated: true)
            }
        })
    }
    - (void)handleAddPaymentOptionButtonTapped {
        // Setup add card view controller
        STPAddCardViewController *addCardViewController = [[STPAddCardViewController alloc] init];
        addCardViewController.delegate = self;
    
        // Present add card view controller
        UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:addCardViewController];
        [self presentViewController:navigationController animated:YES completion:nil];
    }
    
    #pragma mark STPAddCardViewControllerDelegate
    
    - (void)addCardViewControllerDidCancel:(STPAddCardViewController *)addCardViewController {
        // Dismiss add card view controller
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    
    - (void)addCardViewController:(STPAddCardViewController *)addCardViewController didCreateToken:(STPToken *)token completion:(STPErrorBlock)completion {
        [self submitTokenToBackend:token completion:^(NSError *error) {
            if (error) {
                // Show error in add card view controller
                completion(error);
            }
            else {
                // Notify add card view controller that token creation was handled successfully
                completion(nil);
    
                // Dismiss add card view controller
                [self dismissViewControllerAnimated:YES completion:nil];
            }
        }];
    }

    STPPaymentCardTextField

    The STPPaymentCardTextField class is a single-line text field that can be used to collect a credit card number, expiration, CVC, and postal code. The field also performs on-the-fly validation and formatting so that it can provide the user instant feedback. In fact, the STPAddCardViewController class uses it under the hood.

    let paymentCardTextField = STPPaymentCardTextField()
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        // Setup payment card text field
        paymentCardTextField.delegate = self
    
        // Add payment card text field to view
        view.addSubview(paymentCardTextField)
    }
    
    // MARK: STPPaymentCardTextFieldDelegate
    
    func paymentCardTextFieldDidChange(_ textField: STPPaymentCardTextField) {
        // Toggle buy button state
        buyButton.enabled = textField.isValid
    }
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        // Setup payment card text field
        self.paymentCardTextField = [[STPPaymentCardTextField alloc] init];
        self.paymentCardTextField.delegate = self;
    
        // Add payment card text field to view
        [self.view addSubview:self.paymentCardTextField];
    }
    
    #pragma mark STPPaymentCardTextFieldDelegate
    
    - (void)paymentCardTextFieldDidChange:(STPPaymentCardTextField *)textField {
      // Toggle buy button state
      self.buyButton.enabled = textField.isValid
    }

    STPPaymentOptionsViewController

    The STPPaymentOptionsViewController class makes it easy to let your customers manage their payment methods. To initialize it, you'll need to first set up an STPCustomerContext instance. You can present it modally or push it onto a UINavigationController stack. You can also fully customize its appearance via the theme property.

    func handlePaymentOptionsButtonTapped() {
        // Setup customer context
        let customerContext = STPCustomerContext(keyProvider: MyKeyProvider().shared())
    
        // Setup payment options view controller
        let paymentOptionsViewController = STPPaymentOptionsViewController(configuration: STPPaymentConfiguration.shared(), theme: STPTheme.default(), customerContext: customerContext, delegate: self)
    
        // Present payment options view controller
        let navigationController = UINavigationController(rootViewController: paymentOptionsViewController)
        present(navigationController, animated: true)
    }
    
    // MARK: STPPaymentOptionsViewControllerDelegate
    
    func paymentOptionsViewController(_ paymentOptionsViewController: STPPaymentOptionsViewController, didFailToLoadWithError error: Error) {
        // Dismiss payment options view controller
        dismiss(animated: true)
    
        // Present error to user...
    }
    
    func paymentOptionsViewControllerDidCancel(_ paymentOptionsViewController: STPPaymentOptionsViewController) {
        // Dismiss payment options view controller
        dismiss(animated: true)
    }
    
    func paymentOptionsViewControllerDidFinish(_ paymentOptionsViewController: STPPaymentOptionsViewController) {
        // Dismiss payment options view controller
        dismiss(animated: true)
    }
    
    func paymentOptionsViewController(_ paymentOptionsViewController: STPPaymentOptionsViewController, didSelect paymentOption: STPPaymentOption) {
        // Save selected payment option
        selectedPaymentOption = paymentOption
    }
    - (void)handlePaymentOptionsButtonTapped {
        // Setup customer context
        STPCustomerContext *customerContext = [[STPCustomerContext alloc] initWithKeyProvider:[MyKeyProvider shared]];
    
        // Setup payment options view controller
        STPPaymentOptionsViewController *paymentOptionsViewController = [[STPPaymentOptionsViewController alloc] initWithConfiguration:[STPPaymentConfiguration sharedConfiguration] theme:[STPTheme defaultTheme] customerContext:customerContext delegate:self];
    
        // Present payment options view controller
        UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:paymentOptionsViewController];
        [self presentViewController:navigationController animated:YES completion:nil];
    }
    
    #pragma mark STPPaymentOptionsViewControllerDelegate
    
    - (void)paymentOptionsViewController:(STPPaymentOptionsViewController *)paymentOptionsViewController didFailToLoadWithError:(NSError *)error {
        // Dismiss payment options view controller
        [self dismissViewControllerAnimated:YES completion:nil];
    
        // Present error to user...
    }
    
    - (void)paymentOptionsViewControllerDidCancel:(STPPaymentOptionsViewController *)paymentOptionsViewController {
        // Dismiss payment options view controller
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    
    - (void)paymentOptionsViewControllerDidFinish:(STPPaymentOptionsViewController *)paymentOptionsViewController {
        // Dismiss payment options view controller
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    
    - (void)paymentOptionsViewController:(STPPaymentOptionsViewController *)paymentOptionsViewController didSelectPaymentOption:(id<STPPaymentOption>)paymentOption {
        // Save selected payment option
        self.selectedPaymentOption = paymentOption;
    }

    Note that if you're pushing an STPPaymentOptionsViewController instance onto a UINavigationController stack, you should pop it using its dismissWithCompletion: method rather than the standard popViewControllerAnimated:, as it may have pushed an additional add card view controller onto the navigation controller's stack.

    STPShippingAddressViewController

    The STPShippingAddressViewController class automatically handles collecting your user's shipping address and shipping method. You can present it modally or push it onto a UINavigationController stack. You can also fully customize its appearance via the theme property.

    func handleShippingButtonTapped() {
        // Setup shipping address view controller
        let shippingAddressViewController = STPShippingAddressViewController()
        shippingAddressViewController.delegate = self
    
        // Present shipping address view controller
        let navigationController = UINavigationController(rootViewController: shippingAddressViewController)
        present(navigationController, animated: true)
    }
    
    // MARK: STPShippingAddressViewControllerDelegate
    
    func shippingAddressViewControllerDidCancel(_ addressViewController: STPShippingAddressViewController) {
        // Dismiss shipping address view controller
        dismiss(animated: true)
    }
    
    func shippingAddressViewController(_ addressViewController: STPShippingAddressViewController, didEnter address: STPAddress, completion: @escaping STPShippingMethodsCompletionBlock) {
        let upsGroundShippingMethod = PKShippingMethod()
        upsGroundShippingMethod.amount = 0.00
        upsGroundShippingMethod.label = "UPS Ground"
        upsGroundShippingMethod.detail = "Arrives in 3-5 days"
        upsGroundShippingMethod.identifier = "ups_ground"
    
        let fedExShippingMethod = PKShippingMethod()
        fedExShippingMethod.amount = 5.99
        fedExShippingMethod.label = "FedEx"
        fedExShippingMethod.detail = "Arrives tomorrow"
        fedExShippingMethod.identifier = "fedex"
    
        if address.country == "US" {
            let availableShippingMethods = [upsGroundShippingMethod, fedExShippingMethod]
            let selectedShippingMethod = upsGroundShippingMethod
    
            completion(.valid, nil, availableShippingMethods, selectedShippingMethod)
        }
        else {
            completion(.invalid, nil, nil, nil)
        }
    }
    
    func shippingAddressViewController(_ addressViewController: STPShippingAddressViewController, didFinishWith address: STPAddress, shippingMethod method: PKShippingMethod?) {
        // Save selected address and shipping method
        selectedAddress = address
        selectedShippingMethod = method
    
        // Dismiss shipping address view controller
        dismiss(animated: true)
    }
    - (void)handleShippingButtonTapped {
        // Setup shipping address view controller
        STPShippingAddressViewController *shippingAddressViewController = [[STPShippingAddressViewController alloc] init];
        shippingAddressViewController.delegate = self;
    
        // Present shipping address view controller
        UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:shippingAddressViewController];
        [self presentViewController:navigationController animated:YES completion:nil];
    }
    
    #pragma mark STPShippingAddressViewControllerDelegate
    
    - (void)shippingAddressViewControllerDidCancel:(STPShippingAddressViewController *)addressViewController {
        // Dismiss shipping address view controller
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    
    - (void)shippingAddressViewController:(STPShippingAddressViewController *)addressViewController didEnterAddress:(STPAddress *)address completion:(STPShippingMethodsCompletionBlock)completion {
        PKShippingMethod *upsGroundShippingMethod = [PKShippingMethod new];
        upsGroundShippingMethod.amount = [NSDecimalNumber decimalNumberWithString:@"0.00"];
        upsGroundShippingMethod.label = @"UPS Ground";
        upsGroundShippingMethod.detail = @"Arrives in 3-5 days";
        upsGroundShippingMethod.identifier = @"ups_ground";
    
        PKShippingMethod *fedExShippingMethod = [PKShippingMethod new];
        fedExShippingMethod.amount = [NSDecimalNumber decimalNumberWithString:@"5.99"];
        fedExShippingMethod.label = @"FedEx";
        fedExShippingMethod.detail = @"Arrives tomorrow";
        fedExShippingMethod.identifier = @"fedex";
    
        if ([address.country isEqualToString:@"US"]) {
            NSArray *availableShippingMethods = @[upsGroundShippingMethod, fedExShippingMethod];
            PKShippingMethod *selectedShippingMethod = upsGroundShippingMethod;
    
            completion(STPShippingStatusValid, nil, availableShippingMethods, selectedShippingMethod);
        }
        else {
            completion(STPShippingStatusInvalid, nil, nil, nil);
        }
    }
    
    - (void)shippingAddressViewController:(STPShippingAddressViewController *)addressViewController didFinishWithAddress:(STPAddress *)address shippingMethod:(PKShippingMethod *)method {
        // Save selected address and shipping method
        self.selectedAddress = address;
        self.selectedShippingMethod = method;
    
        // Dismiss shipping address view controller
        [self dismissViewControllerAnimated:YES completion:nil];
    }

    Note that if you're pushing an STPShippingAddressViewController instance onto a UINavigationController stack, you should pop it using its dismissWithCompletion: method rather than the standard popViewControllerAnimated:, as it may have pushed an additional shipping method view controller onto the navigation controller's stack.

    Collecting card details

    If you build your own credit card form or collect card details with the STPPaymentCardTextField class, you can tokenize the payment information with the STPAPIClient class. First, assemble an STPCardParams instance with the information you've collected. Then, use the STPAPIClient class to convert that into an STPToken instance which you can then pass to your server.

    let cardParams = STPCardParams()
    cardParams.number = "4242424242424242"
    cardParams.expMonth = 10
    cardParams.expYear = 2021
    cardParams.cvc = "123"
    
    STPAPIClient.shared().createToken(withCard: cardParams) { (token: STPToken?, error: Error?) in
        guard let token = token, error == nil else {
            // Present error to user...
            return
        }
    
        submitTokenToBackend(token, completion: { (error: Error?) in
            if let error = error {
                // Present error to user...
            }
            else {
                // Continue with payment...
            }
        })
    }
    STPCardParams *cardParams = [[STPCardParams alloc] init];
    cardParams.number = @"4242424242424242";
    cardParams.expMonth = 10;
    cardParams.expYear = 2021;
    cardParams.cvc = @"123";
    
    [[STPAPIClient sharedClient] createTokenWithCard:cardParams completion:^(STPToken *token, NSError *error) {
        if (token == nil || error != nil) {
            // Present error to user...
            return;
        }
    
        [self submitTokenToBackend:token completion:^(NSError *error) {
            if (error) {
                // Present error to user...
            }
            else {
                // Continue with payment...
            }
        }];
    }];

    Note that this example assumes you've written a function called submitTokenToBackend which sends an STPToken instance to your server.

    Use the STPToken object to set the token property in the STPPaymentMethodCardParams object, and then confirm the Payment Intent. See Using Payment Intents on iOS for more information.

    let cardParams = STPPaymentMethodCardParams()
    cardParams.token = mySTPToken.tokenId
    let paymentMethodParams = STPPaymentMethodParams(card: cardParams, billingDetails: nil, metadata: nil)
    let paymentIntentParams = STPPaymentIntentParams(clientSecret: clientSecret)
    paymentIntentParams.paymentMethodParams = paymentMethodParams
    STPAPIClient.shared().confirmPaymentIntent(with: paymentIntentParams) { ... }
    STPPaymentMethodCardParams *cardParams = [STPPaymentMethodCardParams new];
    cardParams.token = mySTPToken.tokenId;
    STPPaymentMethodParams *paymentMethodParams = [STPPaymentMethodParams paramsWithCard:cardParams billingDetails:nil metadata:nil];
    STPPaymentIntentParams *paymentIntentParams = [[STPPaymentIntentParams alloc] initWithClientSecret:client_secret];
    paymentIntentParams.paymentMethodParams = paymentMethodParams;
    [[STPAPIClient sharedClient] confirmPaymentIntentWithParams:paymentIntentParams ...];

    Apple Pay integration

    Accepting Apple Pay on Stripe has a lot of similarities to working with cards. When the user approves a payment, your application will receive a PKPayment instance that contains their encrypted card details. You can use the STPAPIClient class to turn a PKPayment instance into an STPToken instance, just as you would with an STPCardParams instance. From there, the token behaves exactly the same way. Let's walk through the full Apple Pay flow.

    First, determine if the user's device supports Apple Pay and they have a card added to their wallet.

    override func viewDidLoad() {
        super.viewDidLoad()
    
        // Toggle apple pay button state
        applePayButton.enabled = Stripe.deviceSupportsApplePay()
    }
    - (void)viewDidLoad {
      [super viewDidLoad];
    
      // Toggle apple pay button state
      self.applePayButton.enabled = [Stripe deviceSupportsApplePay];
    }

    When the user taps "buy", create a PKPaymentRequest instance that describes the payment you want to make. Stripe provides a helper function that does much of the configuration for you.

    func handleApplePayButtonTapped() {
        let merchantIdentifier = "com.merchant.your_application"
        let paymentRequest = Stripe.paymentRequest(withMerchantIdentifier: merchantIdentifier, country: "US", currency: "USD")
    
        // Configure the line items on the payment request
        paymentRequest.paymentSummaryItems = [
            PKPaymentSummaryItem(label: "Fancy Hat", amount: 50.00),
            // The final line should represent your company;
            // it'll be prepended with the word "Pay" (i.e. "Pay iHats, Inc $50")
            PKPaymentSummaryItem(label: "iHats, Inc", amount: 50.00),
        ]
    
        // Continued in next step
    }
    - (void)handleApplePayButtonTapped {
        NSString *merchantIdentifier = @"com.merchant.your_application";
        PKPaymentRequest *paymentRequest = [Stripe paymentRequestWithMerchantIdentifier:merchantIdentifier country:@"US" currency:@"USD"];
    
        paymentRequest.paymentSummaryItems = @[
          [PKPaymentSummaryItem summaryItemWithLabel:@"Fancy Hat" amount:[NSDecimalNumber decimalNumberWithString:@"50.00"]],
          // The final line should represent your company;
          // it'll be prepended with the word "Pay" (i.e. "Pay iHats, Inc $50")
          [PKPaymentSummaryItem summaryItemWithLabel:@"iHats, Inc" amount:[NSDecimalNumber decimalNumberWithString:@"50.00"]],
        ];
    
        // Continued in next step
    }

    Next, create and present a PKPaymentAuthorizationViewController instance with your payment request.

    func handleApplePayButtonTapped() {
        let paymentRequest = ... // From previous step
    
        if Stripe.canSubmitPaymentRequest(paymentRequest) {
            // Setup payment authorization view controller
            let paymentAuthorizationViewController = PKPaymentAuthorizationViewController(paymentRequest: paymentRequest)
            paymentAuthorizationViewController.delegate = self
    
            // Present payment authorization view controller
            present(paymentAuthorizationViewController, animated: true)
        }
        else {
            // There is a problem with your Apple Pay configuration
        }
    }
    - (void)handleApplePayButtonTapped {
        PKPaymentRequest *paymentRequest = ...; // From previous step
    
        if ([Stripe canSubmitPaymentRequest:paymentRequest]) {
            // Setup payment authorization view controller
            PKPaymentAuthorizationViewController *paymentAuthorizationViewController = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:paymentRequest];
            paymentAuthorizationViewController.delegate = self;
    
            // Present payment authorization view controller
            [self presentViewController:paymentAuthorizationViewController animated:YES completion:nil];
        }
        else {
            // There is a problem with your Apple Pay configuration
        }
    }

    Finally, implement the necessary PKPaymentAuthorizationViewControllerDelegate methods using Stripe.

    func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController, didAuthorizePayment payment: PKPayment, completion: @escaping (PKPaymentAuthorizationStatus) -> Void) {
        STPAPIClient.shared().createToken(with: payment) { (token: STPToken?, error: Error?) in
            guard let token = token, error == nil else {
                // Present error to user...
                return
            }
    
            submitTokenToBackend(token, completion: { (error: Error?) in
                if let error = error {
                    // Present error to user...
    
                    // Notify payment authorization view controller
                    completion(.failure)
                }
                else {
                    // Save payment success
                    paymentSucceeded = true
    
                    // Notify payment authorization view controller
                    completion(.success)
                }
            })
        }
    }
    
    func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
        // Dismiss payment authorization view controller
        dismiss(animated: true, completion: {
            if (paymentSucceeded) {
                // Show a receipt page...
            }
        })
    }
    - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didAuthorizePayment:(PKPayment *)payment completion:(void (^)(PKPaymentAuthorizationStatus))completion {
        [[STPAPIClient sharedClient] createTokenWithPayment:payment completion:^(STPToken *token, NSError *error) {
            if (token == nil || error != nil) {
                // Present error to user...
                return;
            }
    
            [self submitTokenToBackend:token completion:^(NSError *error) {
                if (error) {
                    // Present error to user...
    
                    // Notify payment authorization view controller
                    completion(PKPaymentAuthorizationStatusFailure);
                }
                else {
                    // Save payment success
                    self.paymentSucceeded = YES;
    
                    // Notify payment authorization view controller
                    completion(PKPaymentAuthorizationStatusSuccess);
                }
            }];
        }];
    }
    
    - (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller {
        [self dismissViewControllerAnimated:YES completion:^{
            if (self.paymentSucceeded) {
                // Show a receipt page...
            }
        }];
    }

    Next steps

    If you'd like to read more about Apple Pay and its other configuration options, we recommend the NSHipster article on Apple Pay and the Apple Pay Within Apps presentation from WWDC 2015.

    Questions?

    We're always happy to help with code or other questions you might have. Search our documentation, contact support, or connect with our sales team. You can also chat live with other developers in #stripe on freenode.

    Was this page helpful? Yes No

    Send

    Thank you for helping improve Stripe's documentation. If you need help or have any questions, please consider contacting support.

    On this page