Collecting Payment Methods in iOS (beta)

If you’re building an iOS app and want to accept payments, our iOS library can make it easier. We remove the burden of PCI compliance by making sure sensitive credit card information never touches your servers. Just a token associated with that payment method. Our library also supports Apple Pay so you can enable users to make payments without entering their credit card info.

Accepting payments in your app involves three steps:

  1. Collect credit card information from your customer.
  2. Tokenizing your customer’s payment information.
  3. Sending the token to your backend to initiate a charge.

We have a full example application if you want to see it all put together. You can test the entire process with a credit card by creating a “test” environment in your Spreedly account and using test card data. However, with Apple Pay you can only fully test creating a payment method on a mobile device and not within the simulator.

Getting started

Start by installing the library. We recommend using CocoaPods since it makes it easy to keep your app’s dependencies up to date. If you haven’t set up CocoaPods before, checkout their website to get it installed.

Once CocoaPods is setup, add the Spreedly pod to your Podfile and run pod install.


source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
use_frameworks!

target 'YourApp' do
  pod 'Spreedly'
end

Using a custom credit card form

If you’re building a custom credit card form, at some point within your app you’ll need to accept the credit card number, verification value, expiration month, expiration year, and the cardholders first and last name. Once you’ve done that, you’ll need to instantiate a Spreedly Credit Card object and populate it with the captured values.


class ViewController: UIViewController {

    @IBOutlet weak var cardFirstName: CCTextField!
    @IBOutlet weak var cardLastName: CCTextField!
    @IBOutlet weak var cardNumber: CCTextField!
    @IBOutlet weak var cardExpiration: CCTextField!
    @IBOutlet weak var cardVerificationValue: CCTextField!

    @IBAction func handlePurchaseTapped(sender: AnyObject) {
        let creditCard = CreditCard()
        creditCard.firstName = self.cardFirstName.text!
        creditCard.lastName = self.cardLastName.text!
        creditCard.number = self.cardNumber.text!
        creditCard.month = self.cardExpMonth.text!
        creditCard.year = self.cardExpYear.text!
        creditCard.verificationValue = self.cardVerificationValue.text!

      // We'll go more in depth on tokenizing in the next section
    }
}

You may want to include additional validation if you need additional information like a billing address. However, if you’ve received a valid credit card from the user, you’re ready to move on to tokenizing the payment data.

Tokenizing credit card information

Just like with Apple Pay, the Spreedly API client also accepts a credit card object to be tokenized.


func handlePurchaseTapped(sender: AnyObject) {
  client = SpreedlyAPIClient(environmentKey: "your-env-key")
  client.createPaymentMethodTokenWithCreditCard(creditCard) { paymentMethod, error -> Void in
    if error != nil {
      // If there's an error while creating a token, you can handle it here.
    } else {
      if let token = paymentMethod!.token as? String {
        // Now you can send the token to your backend server
      }
    }
  }
}

The Spreedly API client returns a PaymentMethod object (or an NSError detailing what went wrong) as a parameter to your completion block. That PaymentMethod includes the token you will use as a reference in your backend to charge your customer. The PaymentMethod mirrors what you would expect to receive from the Spreedly API if you had made the request yourself. You can see all attributes of a payment method in the documentation.

Sending the token to your server

Lastly, grab the token from the passed in PaymentMethod object and send that to your backend. Server side, you need to have an endpoint that accepts a the payment method token. From there you can follow our guide to making a purchase. In addition, if you want to save this payment method in your customer’s account, you can make a request to retain that payment method and it will be securely saved for future use.


client = SpreedlyAPIClient(environmentKey: "your-env-key")
client.createPaymentMethodTokenWithCreditCard(creditCard) { paymentMethod, error -> Void in
  if error != nil {
      // If there's an error while creating a token, you can handle it here.
    } else {
      if let token = paymentMethod!.token as? String {
        createBackendCharge(paymentMethod: PaymentMethod) { data, response error -> Void in
          if error != nil {
            // handle error
          } else {
            // handle success
          }
        }
      }
    }
  }
}

All authenticated requests must come from you secure server environment as access tokens should not be stored within your app for security reasons.

Using Apple Pay

To support Apple Pay, you need enable the Apple Pay capabiliy in your app and obtain a merchant ID from Apple first. You can find more information on setting up Apple Pay in the Apple developer docs.

If you do decide to support Apple Pay we strongly recommend using it in conjunction with a standard credit card form as a fallback if Apple Pay is not available.

Collecting payment information

At this point, we’re assuming you’ve enabled the Apple Pay capability in your app and followed the necessary steps to setup and display the PKPaymentAuthorizationViewController. If so, you’re ready to tokenize your customer’s Apple Pay payment information.

You’ll need to conform your view controller to the PKPaymentAuthorizationViewControllerDelegate protocol in order to receive the PKPayment returned from a customer authorization via Touch ID or a passcode. The PKPayment object is what you’ll eventually send to the Spreedly iOS library to extract the payment information and tokenize.


class ViewController: UIViewController, PKPaymentAuthorizationViewControllerDelegate {

    func paymentAuthorizationViewController(controller: PKPaymentAuthorizationViewController, didAuthorizePayment payment: PKPayment, completion:((PKPaymentAuthorizationStatus) -> Void)) {
      // We'll go more in depth on this part in the tokenizing payment methods section
    }

    // This is part of the protocol and used for updating the Apple Pay UI on successes or failures
    func paymentAuthorizationViewControllerDidFinish(controller: PKPaymentAuthorizationViewController) {
      dismissViewControllerAnimated(true, completion: nil)
    }
}

Once you’ve successfully received a PKPayment, you’re ready to tokenize that payment information.

Tokenizing Apple Pay payment information

Start by configuring your client with the correct environment key. We recommend setting the environment key before using your client so if you have multiple environments, you can dynamically set the environment you need.


func paymentAuthorizationViewController(controller: PKPaymentAuthorizationViewController, didAuthorizePayment payment: PKPayment, completion:((PKPaymentAuthorizationStatus) -> Void)) {
  let client = SpreedlyAPIClient(environmentKey: "your-env-key")
  client.createPaymentMethodTokenWithApplePay(payment) { paymentMethod, error -> Void in
    if error != nil {
      // if there's a failure, send the .Failure status to update the Apple Pay UI
      completion(PKPaymentAuthorizationStatus.Failure)
    } else {
      if let token = paymentMethod!.token as? String {
        // Here is where you can send a request to your backend to finish the charge with
        // an authenticated API call from your server passing the payment method token
      }
    }
  }
}

The Spreedly API client returns a PaymentMethod object (or an NSError detailing what went wrong) as a parameter to your completion block. That PaymentMethod includes the token you will use as a reference in your backend to charge your customer. The PaymentMethod mirrors what you would expect to receive from the Spreedly API if you had made the request yourself. You can see all attributes of a payment method in the documentation.

Sending the token to your server

Lastly, send that token to your backend and pass the .Success or .Failure status accordingly to the paymentAuthorizationViewControllerDidFinish to update the Apple Pay UI.


func paymentAuthorizationViewController(controller: PKPaymentAuthorizationViewController, didAuthorizePayment payment: PKPayment, completion:((PKPaymentAuthorizationStatus) -> Void)) {
  let client = SpreedlyAPIClient(environmentKey: "your-env-key")
  client.createPaymentMethodTokenWithApplePay(payment) { paymentMethod, error -> Void in
    if error != nil {
          // if there's a failure, send the .Failure status to update the Apple Pay UI
          completion(PKPaymentAuthorizationStatus.Failure)
    }
    else {
      if let token = paymentMethod!.token! as? String {
        createBackendCharge(paymentMethod: PaymentMethod) { data, response error -> Void in
          if error != nil {
            completion(PKPaymentAuthorizationStatus.Failure)
          } else {
            completion(PKPaymentAuthorizationStatus.Success)
          }
        }
      }
    }
  }
}

Server side, you need to have an endpoint that accepts a the payment method token. From there you can follow our guide to making a purchase. In addition, if you want to save this payment method in your customer’s account, you can make a request to retain that payment method and it will be securely saved for future use.

Error Handling

There are three cases in which you might run into an error while using the Spreedly iOS library:

  1. If there is a network connection problem
  2. There is an error with your request
  3. The Spreedly iOS library was unable to parse the response from the API.

In all of these cases, the completion block you specify to the Spreedly iOS library will be passed an NSError object which you can use to decide what you want to do next.

If there are API response errors, you’ll most likely see issues with missing or incorrectly formatted fields. Such as Year can't be blank. The exact error will be in the NSError’s userInfo attribute with the domain com.spreedly.lib.