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. If you need help after reading this, search our documentation or check out answers to common questions. You can even chat live with other developers in #stripe on freenode.

    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 client like AFNetworking or Alamofire to talk to your own API.

    STPAddCardViewController

    STPAddCardViewController automatically handles both collecting and tokenizing your user's payment information. You can present it modally, or push onto a UINavigationController stack. You can also fully customize its appearance via the theme property. For more information, see our main iOS tutorial.

    func buyButtonTapped() {
        let addCardViewController = STPAddCardViewController()
        addCardViewController.delegate = self
        // STPAddCardViewController must be shown inside a UINavigationController.
        let navigationController = UINavigationController(rootViewController: addCardViewController)
        self.present(navigationController, animated: true, completion: nil)
    }
    
    // MARK: STPAddCardViewControllerDelegate
    
    func addCardViewControllerDidCancel(_ addCardViewController: STPAddCardViewController) {
        self.dismiss(animated: true, completion: nil)
    }
    
    func addCardViewController(_ addCardViewController: STPAddCardViewController, didCreateToken token: STPToken, completion: STPErrorBlock) {
        self.submitTokenToBackend(token, completion: { (error: Error?) in
            if let error = error {
                completion(error)
            } else {
                self.dismiss(animated: true, completion: {
                    self.showReceiptPage()
                    completion(nil)
                })
            }
        })
    }
    - (void)payButtonTapped {
        STPAddCardViewController *addCardViewController = [[STPAddCardViewController alloc] init];
        addCardViewController.delegate = self;
        // STPAddCardViewController must be shown inside a UINavigationController.
        UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:addCardViewController];
        [self presentViewController:navigationController animated:YES completion:nil];
    }
    
    #pragma mark STPAddCardViewControllerDelegate
    
    - (void)addCardViewControllerDidCancel:(STPAddCardViewController *)addCardViewController {
        [self dismissViewControllerAnimated:YES completion:nil];
    }
    
    - (void)addCardViewController:(STPAddCardViewController *)addCardViewController
                   didCreateToken:(STPToken *)token
                       completion:(STPErrorBlock)completion {
        [self submitTokenToBackend:token completion:^(NSError *error) {
            if (error) {
                completion(error);
            } else {
                [self dismissViewControllerAnimated:YES completion:^{
                    [self showReceiptPage];
                }];
            }
        }];
    }

    STPPaymentCardTextField

    This is a smaller and more flexible piece of UI than STPAddCardViewController (in fact, STPAddCardViewController uses it under the hood). It's a single-line text field that can be used to collect a credit card number, expiration, and CVC. It performs on-the-fly validation and formatting of card numbers.

    override func viewDidLoad() {
        super.viewDidLoad()
        let paymentField = STPPaymentCardTextField(frame: CGRect(x: 10, y: 10, width:300, height: 44))
        paymentField.delegate = self
        self.view.addSubview(paymentField)
    }
    
    // MARK: STPPaymentCardTextFieldDelegate
    
    func paymentCardTextFieldDidChange(_ textField: STPPaymentCardTextField) {
        print("Card number: \(textField.cardParams.number) Exp Month: \(textField.cardParams.expMonth) Exp Year: \(textField.cardParams.expYear) CVC: \(textField.cardParams.cvc)")
        self.buyButton.enabled = textField.isValid
    }
    - (void)viewDidLoad {
        [super viewDidLoad];
        STPPaymentCardTextField *paymentField = [[STPPaymentCardTextField alloc] initWithFrame:CGRectMake(10, 10, 300, 44)];
        paymentField.delegate = self;
        [self.view addSubview:paymentField];
    }
    
    #pragma mark STPPaymentCardTextFieldDelegate
    
    - (void)paymentCardTextFieldDidChange:(STPPaymentCardTextField *)textField {
        NSLog(@"Card number: %@ Exp Month: %@ Exp Year: %@ CVC: %@", textField.cardParams.number, @(textField.cardParams.expMonth), @(textField.cardParams.expYear), textField.cardParams.cvc);
        self.buyButton.enabled = textField.isValid;
    }

    STPPaymentMethodsViewController

    STPPaymentMethodsViewController makes it easy to let your customer manage their payment methods. To initialize it, you'll need to first set up an STPCustomerContext – for more information, see our standard guide. You can present STPPaymentMethodsViewController modally, or push it onto a UINavigationController stack. You can also fully customize its appearance via the theme property. For more information, see our main iOS tutorial.

    func paymentMethodsButtonTapped() {
        let paymentMethodsViewController = STPPaymentMethodsViewController()
        paymentMethodsViewController.delegate = self
        // STPShippingAddressViewController must be shown inside a UINavigationController.
        let navigationController = UINavigationController(rootViewController: paymentMethodsViewController)
        self.present(navigationController, animated: true, completion: nil)
    }
    
    // MARK: STPPaymentMethodsViewControllerDelegate
    
    func paymentMethodsViewControllerDidCancel(_ paymentMethodsViewController: STPPaymentMethodsViewController) {
        paymentMethodsViewController.dismiss(completion: nil)
    }
    
    func paymentMethodsViewControllerDidFinish(_ paymentMethodsViewController: STPPaymentMethodsViewController) {
        paymentMethodsViewController.dismiss(completion: nil)
    }
    - (void)paymentMethodsButtonTapped {
        STPPaymentMethodsViewController *paymentMethodsViewController = [[STPPaymentMethodsViewController alloc] initWithConfiguration:[STPPaymentConfiguration sharedConfiguration]
                                                                                                                                 theme:[STPTheme defaultTheme]
                                                                                                                       customerContext:[STPCustomerContext sharedInstance]
                                                                                                                              delegate:self];
        // STPPaymentMethodsViewController must be shown inside a UINavigationController.
        UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:paymentMethodsViewController];
        [self presentViewController:navigationController animated:YES completion:nil];
    }
    
    #pragma mark STPPaymentMethodsViewControllerDelegate
    
    - (void)paymentMethodsViewControllerDidCancel:(STPPaymentMethodsViewController *)paymentMethodsViewController {
        [paymentMethodsViewController dismissWithCompletion:nil];
    }
    
    - (void)paymentMethodsViewControllerDidFinish:(STPPaymentMethodsViewController *)paymentMethodsViewController {
        [paymentMethodsViewController dismissWithCompletion:nil];
    }

    Note that if you're pushing STPPaymentMethodsViewController 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

    STPShippingAddressViewController automatically handles collecting your user's shipping address and shipping method. You can present it modally, or push onto a UINavigationController stack. You can also fully customize its appearance via the theme property. For more information, see our main iOS tutorial.

    func shippingButtonTapped() {
        let shippingViewController = STPShippingAddressViewController()
        shippingViewController.delegate = self
        // STPShippingAddressViewController must be shown inside a UINavigationController.
        let navigationController = UINavigationController(rootViewController: shippingViewController)
        self.present(navigationController, animated: true, completion: nil)
    }
    
    // MARK: STPShippingAddressViewControllerDelegate
    
    func shippingAddressViewControllerDidCancel(_ addressViewController: STPShippingAddressViewController) {
        addressViewController.dismiss(completion: nil)
    }
    
    func shippingAddressViewController(_ addressViewController: STPShippingAddressViewController, didEnter address: STPAddress, completion: STPShippingMethodsCompletionBlock) {
        let upsGround = PKShippingMethod()
        upsGround.amount = 0
        upsGround.label = "UPS Ground"
        upsGround.detail = "Arrives in 3-5 days"
        upsGround.identifier = "ups_ground"
        let fedEx = PKShippingMethod()
        fedEx.amount = 5.99
        fedEx.label = "FedEx"
        fedEx.detail = "Arrives tomorrow"
        fedEx.identifier = "fedex"
    
        if address.country == "US" {
            completion(.valid, nil, [upsGround, fedEx], upsGround)
        }
        else {
            completion(.invalid, nil, nil, nil)
        }
    }
    
    func shippingAddressViewController(_ addressViewController: STPShippingAddressViewController, didFinishWith address: STPAddress, shippingMethod method: PKShippingMethod?) {
        // Dismiss the view controller after updating your app with the user's shipping info
        addressViewController.dismiss(completion: nil)
    }
    - (void)shippingButtonTapped {
        STPShippingAddressViewController *shippingViewController = [STPShippingAddressViewController new];
        shippingViewController.delegate = self;
        // STPShippingAddressViewController must be shown inside a UINavigationController.
        UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:shippingViewController];
        [self presentViewController:navigationController animated:YES completion:nil];
    }
    
    #pragma mark STPShippingAddressViewControllerDelegate
    
    - (void)shippingAddressViewControllerDidCancel:(STPShippingAddressViewController *)addressViewController {
        [addressViewController dismissWithCompletion:nil];
    }
    
    - (void)shippingAddressViewController:(STPShippingAddressViewController *)addressViewController didEnterAddress:(STPAddress *)address completion:(STPShippingMethodsCompletionBlock)completion {
        PKShippingMethod *upsGround = [PKShippingMethod new];
        upsGround.amount = [NSDecimalNumber decimalNumberWithString:@"0"];
        upsGround.label = @"UPS Ground";
        upsGround.detail = @"Arrives in 3-5 days";
        upsGround.identifier = @"ups_ground";
        PKShippingMethod *fedEx = [PKShippingMethod new];
        fedEx.amount = [NSDecimalNumber decimalNumberWithString:@"5.99"];
        fedEx.label = @"FedEx";
        fedEx.detail = @"Arrives tomorrow";
        fedEx.identifier = @"fedex";
        if ([address.country isEqualToString:@"US"]) {
            completion(STPShippingStatusValid, nil, @[upsGround, fedEx], upsGround);
        }
        else {
            completion(STPShippingStatusInvalid, nil, nil, nil);
        }
    }
    
    - (void)shippingAddressViewController:(STPShippingAddressViewController *)addressViewController didFinishWithAddress:(STPAddress *)address shippingMethod:(PKShippingMethod *)method {
        // Dismiss the view controller after updating your app with the user's shipping info
        [addressViewController dismissWithCompletion:nil];
    }

    Note that if you're pushing STPShippingAddressViewController 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 STPPaymentCardTextField or building your own credit card form, you can use the STPAPIClient class to handle tokenizing those details. First, assemble an STPCardParams object with the information you've collected. Then, use STPAPIClient to convert that into an STPToken which you can then pass to your server. (Note: the examples in this guide assume you've written a function called submitTokenToBackend:completion: which sends your STPToken to your server).

    let cardParams = STPCardParams()
    cardParams.number = "4242424242424242"
    cardParams.expMonth = 10
    cardParams.expYear = 2018
    cardParams.cvc = "123"
    STPAPIClient.shared().createTokenWithCard(cardParams) { (token, error) in
        if let error = error {
            // show the error to the user
        } else if let token = token {
            self.submitTokenToBackend(token, completion: { (error: Error?) in
                if let error = error {
                    // show the error to the user
                } else {
                    // show a receipt page
                }
            })
        }
    }
    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 (error) {
            // show the error, maybe by presenting an alert to the user
        } else {
            [self submitTokenToBackend:token completion:^(NSError *error) {
                if (error) {
                    // show the error
                } else {
                    [self showReceiptPage];
                }
            }];
        }
    }];

    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 object that contains their encrypted card details. You can use STPAPIClient to turn a PKPayment into an STPToken, just as you would with an STPCardParams object. 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()
        self.applePayButton.enabled = Stripe.deviceSupportsApplePay()
    }
    - (void)viewDidLoad {
      [super viewDidLoad];
      self.applePayButton.enabled = [Stripe deviceSupportsApplePay];
    }

    Next, when the user taps "buy", create a PKPaymentRequest object that describes the payment you want to make. Stripe provides a helper function that does much of the configuration for you. Note: to obtain an Apple Merchant ID, read our Apple Pay setup guide.

    func applePayTapped() {
        let paymentRequest = Stripe.paymentRequestWithMerchantIdentifier("com.merchant.your_application")
        // Configure the line items on the payment sheet
        paymentRequest?.paymentSummaryItems = [
            PKPaymentSummaryItem(label: "Fancy hat", amount: NSDecimalNumber(string: "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: NSDecimalNumber(string: "50.00")),
        ]
        // To be continued
    }
    - (void)applePayTapped {
      PKPaymentRequest *paymentRequest = [Stripe paymentRequestWithMerchantIdentifier:@"com.merchant.your_application"];
      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"]],
      ];
      // To be continued
    }

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

    func applePayTapped() {
        let paymentRequest = ... // see above
        if let paymentRequest = paymentRequest where Stripe.canSubmitPaymentRequest(paymentRequest) {
            let paymentAuthorizationVC = PKPaymentAuthorizationViewController(paymentRequest: paymentRequest)
            paymentAuthorizationVC.delegate = self
            self.present(paymentAuthorizationVC, animated: true, completion: nil)
        } else {
            // there is a problem with your Apple Pay configuration.
        }
    }
    - (void)applePayTapped {
      PKPaymentRequest *paymentRequest = ...; // see above
      if ([Stripe canSubmitPaymentRequest:paymentRequest]) {
        PKPaymentAuthorizationViewController *paymentAuthorizationVC = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:paymentRequest];
        paymentAuthorizationVC.delegate = self
        [self presentViewController:paymentAuthorizationVC animated:YES completion:nil];
        } else {
            // there is a problem with your Apple Pay configuration.
        }
    }

    Finally, implement the necessary PKPaymentAuthorizationViewControllerDelegate methods using Stripe:

    // MARK: PKPaymentAuthorizationViewControllerDelegate
    
    var paymentSucceeded: Bool = false
    
    func paymentAuthorizationViewController(_ controller: PKPaymentAuthorizationViewController,
        didAuthorizePayment payment: PKPayment, completion: @escaping (PKPaymentAuthorizationStatus) -> Void) {
    
        STPAPIClient.sharedClient().createTokenWithPayment(payment) { (token, error) in
            self.submitTokenToBackend(token, completion: { (error: Error?) in
                if let error = error {
                    completion(.Failure)
                } else {
                    self.paymentSucceeded = true
                    completion(.Success)
                }
            })
        }
    }
    
    func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) {
        self.dismiss(animated: true, completion: {
            if (self.paymentSucceeded) {
                // show a receipt page
            }
        })
    }
    #pragma mark PKPaymentAuthorizationViewControllerDelegate
    
    - (void)paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller didAuthorizePayment:(PKPayment *)payment completion:(void (^)(PKPaymentAuthorizationStatus))completion {
        [[STPAPIClient sharedClient] createTokenWithPayment:payment completion:^(STPToken * _Nullable token, NSError * _Nullable error) {
            [self submitTokenToBackend:token completion:^(NSError *error) {
                if (error) {
                    completion(PKPaymentAuthorizationStatusFailure);
                } else {
                    completion(PKPaymentAuthorizationStatusSuccess);
                    self.paymentSuceeded = YES; // define this property in your header
                }
            }];
        }];
    }
    
    - (void)paymentAuthorizationViewControllerDidFinish:(PKPaymentAuthorizationViewController *)controller {
        [self dismissViewControllerAnimated:YES completion:^{
          if (self.paymentSucceeded) {
            [self showReceiptPage];
          }
        }];
    }

    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.