Custom iOS Integration Guide

    Accept payments in iPhone and iPad apps, with built-in support for Apple Pay. This guide covers how to use the individual components of our SDK. If you'd like to use Sources with the iOS SDK, we have a separate guide to help you get started.

    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. For more information about that, see our standard integration guide.

    func handleAddPaymentMethodButtonTapped() {
        // 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)handleAddPaymentMethodButtonTapped {
        // 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
    }

    STPPaymentMethodsViewController

    The STPPaymentMethodsViewController 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. For more information about that, see our standard integration guide.

    func handlePaymentMethodsButtonTapped() {
        // Setup customer context
        let customerContext = STPCustomerContext(keyProvider: MyKeyProvider().shared())
    
        // Setup payment methods view controller
        let paymentMethodsViewController = STPPaymentMethodsViewController(configuration: STPPaymentConfiguration.shared(), theme: STPTheme.default(), customerContext: customerContext, delegate: self)
    
        // Present payment methods view controller
        let navigationController = UINavigationController(rootViewController: paymentMethodsViewController)
        present(navigationController, animated: true)
    }
    
    // MARK: STPPaymentMethodsViewControllerDelegate
    
    func paymentMethodsViewController(_ paymentMethodsViewController: STPPaymentMethodsViewController, didFailToLoadWithError error: Error) {
        // Dismiss payment methods view controller
        dismiss(animated: true)
    
        // Present error to user...
    }
    
    func paymentMethodsViewControllerDidCancel(_ paymentMethodsViewController: STPPaymentMethodsViewController) {
        // Dismiss payment methods view controller
        dismiss(animated: true)
    }
    
    func paymentMethodsViewControllerDidFinish(_ paymentMethodsViewController: STPPaymentMethodsViewController) {
        // Dismiss payment methods view controller
        dismiss(animated: true)
    }
    
    func paymentMethodsViewController(_ paymentMethodsViewController: STPPaymentMethodsViewController, didSelect paymentMethod: STPPaymentMethod) {
        // Save selected payment method
        selectedPaymentMethod = paymentMethod
    }
    - (void)handlePaymentMethodsButtonTapped {
        // Setup customer context
        STPCustomerContext *customerContext = [[STPCustomerContext alloc] initWithKeyProvider:[MyKeyProvider shared]];
    
        // Setup payment methods view controller
        STPPaymentMethodsViewController *paymentMethodsViewController = [[STPPaymentMethodsViewController alloc] initWithConfiguration:[STPPaymentConfiguration sharedConfiguration] theme:[STPTheme defaultTheme] customerContext:customerContext delegate:self];
    
        // Present payment methods view controller
        UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:paymentMethodsViewController];
        [self presentViewController:navigationController animated:YES completion:nil];
    }
    
    #pragma mark STPPaymentMethodsViewControllerDelegate
    
    - (void)paymentMethodsViewController:(STPPaymentMethodsViewController *)paymentMethodsViewController didFailToLoadWithError:(NSError *)error {
        // Dismiss payment methods view controller
        [self dismissViewControllerAnimated:YES completion:nil];
    
        // Present error to user...
    }
    
    - (void)paymentMethodsViewControllerDidCancel:(STPPaymentMethodsViewController *)paymentMethodsViewController {
        // Dismiss payment methods view controller
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    
    - (void)paymentMethodsViewControllerDidFinish:(STPPaymentMethodsViewController *)paymentMethodsViewController {
        // Dismiss payment methods view controller
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    
    - (void)paymentMethodsViewController:(STPPaymentMethodsViewController *)paymentMethodsViewController didSelectPaymentMethod:(id<STPPaymentMethod>)paymentMethod {
        // Save selected payment method
        self.selectedPaymentMethod = paymentMethod;
    }

    Note that if you're pushing an STPPaymentMethodsViewController 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. For more information about that, see our standard integration guide.

    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.

    STPAPIClient + STPCardParams

    If you're collecting your users' card details by using the STPPaymentCardTextField class or building your own credit card form, you can use the STPAPIClient class to handle tokenizing those details. 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 = 2018
    cardParams.cvc = "123"
    
    STPAPIClient.shared().createTokenWithCard(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 = 2018;
    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.

    Apple Pay Utilities

    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, you can use Stripe to 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];
    }

    Next, 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. To obtain an Apple Merchant ID, see our Apple Pay setup guide.

    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.paymentSuceeded = YES;
    
                    // Notify payment authorization view controller
                    completion(PKPaymentAuthorizationStatusSuccess);
                }
            }];
        }];
    }
    
    - (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller {
        [self dismissViewControllerAnimated:YES completion:^{
            if (self.paymentSucceeded) {
                // Show a receipt page...
            }
        }];
    }

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