iOS Integration (Custom)

Accept payments in iPhone and iPad apps, with built-in support for Apple Pay.

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.

Note: We also publish a full SDK reference that documents every single method and property of the classes described here.

STPAddCardViewController

This is a UIViewController subclass that you can present 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. It will automatically handle both collecting and tokenizing your user's payment information, and notify its delegate when it's done.

func buyButtonTapped() {
    let addCardViewController = STPAddCardViewController()
    addCardViewController.delegate = self
    // STPAddCardViewController must be shown inside a UINavigationController.
    let navigationController = UINavigationController(rootViewController: addCardViewController)
    self.presentViewController(navigationController, animated: true, completion: nil)
}

// MARK: STPAddCardViewControllerDelegate

func addCardViewControllerDidCancel(addCardViewController: STPAddCardViewController) {
    self.dismissViewControllerAnimated(true, completion: nil)
}

func addCardViewController(addCardViewController: STPAddCardViewController, didCreateToken token: STPToken, completion: STPErrorBlock) {
    self.submitTokenToBackend(token, completion: { (error: NSError?) in
        if let error = error {
            completion(error)
        } else {
            self.dismissViewControllerAnimated(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];
            }];
        }
    }];
}

STPAddCardViewController will also allow users to securely autofill their card details if they've ever made a purchase from another app that uses STPAddCardViewController or from Stripe Checkout on the web (and opted in, of course). Upon typing their email address, they'll be sent an SMS with a code and prompted to enter it into a form that appears. We've found this to have a significant positive impact on conversion rates, but if you'd like to disable it, just add the following code to your app delegate:

STPPaymentConfiguration.sharedConfiguration().smsAutofillDisabled = true
[[STPPaymentConfiguration sharedConfiguration] setSmsAutofillDisabled:YES];

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 is useful 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;
}

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 uses a networking library to POST your STPToken to your server).

let cardParams = STPCardParams()
cardParams.number = "4242424242424242"
cardParams.expMonth = 10
cardParams.expYear = 2018
cardParams.cvc = "123"
STPAPIClient.sharedClient().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: NSError?) 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 Apple Pay is supported on the user's device and if they have a card loaded into 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.presentViewController(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: (PKPaymentAuthorizationStatus) -> Void) {
    STPAPIClient.sharedClient().createTokenWithPayment(payment) { (token, error) in
        self.submitTokenToBackend(token, completion: { (error: NSError?) in
            if let error = error {
                completion(.Failure)
            } else {
                self.paymentSucceeded = true
                completion(.Success)
            }
        })
    }
}

func paymentAuthorizationViewControllerDidFinish(controller: PKPaymentAuthorizationViewController) {
    self.dismissViewControllerAnimated(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.