iOS integration

Use these guidelines to incorporate the mobile SDK into your iOS mobile app. See the iOS API for additional details about implementing each screen.

  1. Prepare for integration
  2. Add the SDK to your project
  3. Add permissions
  4. Import the SDK
  5. Add the Amount screen (optional)
  6. Add the Card Entry screen (required)
  7. Add the Customer Info screen (recommended)
  8. Trim the target architectures

You should also review the orientation guidelines.

1: Prepare for integration

Before you begin:

2: Add the SDK to your project

You must add the SDK to your project before you can start working with it. For more information, contact AffiniPay Support.

3: Add permissions

To support an AffiniPay mobile card reader, you must add an entry to the Info.plist file for for the key NSMicrophoneUsageDescription and a String value that describes why the microphone permissions on the device are needed, such as “Credit Card Reader”.

In addition, for the Bluetooth card reader, you must add entries to the Info.plist file for:

4: Import the SDK

import AffiniPaySDK

5: Add the Amount screen

How the amount to be paid is determined is application specific. Your app might allow the user to input a dollar amount or possibly sum the contents of a shopping cart. Once the amount is known, then your app can invoke our card-entry process to begin the payment process.

6: Add the Card Entry screen

You must include the Card Entry screen that enables the merchant to either swipe to provide card information or manually enter the card details. This screen is required to charge with the AffiniPay Payment Gateway.

To add the Card Entry screen:

  1. Create InitParams.
    let afpTokenInitParams = AFPTokenizationInitParams(publicKey: getPublicKey(),
                                 amount: "100", // cents
                                 accountName: accountName, // name of account to deposit
                                 trustAccount: true,  // or false
                                 requireCvv: true, // or false
                                 requireSwipeCvv: true, // or false
                                 disableCardReader: false, // or false
                                 autoShowHideNavigationBar: true)
    

  2. Create AFPCardEntryVC and ClientDetailsController and pass InitParams in the getCardEntryVC method.
    public class MainViewController: UIViewController {
       var clientVC: ClientDetailsController?
       private var afpCardEntryVC: AFPCardEntryVC?
    
       public override func viewDidLoad() {
           super.viewDidLoad()
       }
    
     func showCardEntry() {
       // Instantiate card entry view controller
           self.afpCardEntryVC = AffiniPaySDK.getCardEntryVC(afpTokenInitParams, onReturnSwiperData: { (swiperData) in
               // swiperData
               print("swiperData: \(swiperData)")
    
           }, onReturnCardData: { (data) in
               // data
               print("data: \(data)")
    
               // show client details page            
               // ... the client details screen is going to be created by the partners
    
               self.clientVC = self.storyboard.instantiateViewController(withIdentifier: "ClientDetailsController") as? ClientDetailsController
               self.clientVC!.delegate = self // this is needed to get callback from the client details
               self.afpCardEntryVC?.navigationController?.pushViewController(self.clientVC!, animated: true)
    
           }, onReturnPaymentToken: { (result) in
    
               self.clientVC?.stopLoading() // stop loading at client details page
               if (result!.error) != nil {
                   // error
               } else {
                   print("Amount: \(amountString)")
    
                   print("One time token: \(tokenId)"                
                   // step: Create a charge on the backend using one token, account id, and amount                
                   // ... The network layer code to create charge
    
                   // step: Show Signature screen on successful charge                
                   // ... the signature screen is going to be created by the partners                
                   // ... the flow ends here
    
           }, onReset: {
               // callback on reset
               print("Block Called => onReset")
           }, onDismiss: {
               // callback on dismis
               print("Block Called => onDismiss")
           }, onCancelCvvAlert: {
               // callback on dismiss
               print("Block Called => onCancelCvvAlert")
               self.afpCardEntryVC!.dismiss()
           })
       }
      }

To submit the charge:

After the AFPCardEntryVC view controller returns the AFPChargeResult object:

  1. Your mobile app must pass the amountString and tokenID to your backend server.
  2. Your backend server must POST the charge details and other required information (such as the merchant’s secret key) to the charges endpoint of the AffiniPay Payment Gateway.

Note: To ensure the security of payment details, never make a call to the AffiniPay Payment Gateway directly from your mobile app.

See AffiniPay’s sample mobile and backend apps for an example of how this can be implemented.

7: Add the Customer Info screen

Prior to completing a transaction, additional customer information can be passed along with the transaction. Some customer information fields are required, and collecting them may also be application specific. For example, your application may already have all required customer information in its database. In which case it just needs to pass it along during the tokenization process. Other applications will need to provide a user interface for collecting this information. Be sure to gather the appropriate cardholder details.

To add a Customer Info screen:

  1. Create ClientDetailsController.swift and handle the Callback.
    import UIKit
    import AffiniPaySDK.AFPCustomerInfo
    protocol ClientDetailsControllerDelegate {
     func onSubmitClientInfo(customerInfo: AFPCustomerInfo)
    }
    class ClientDetailsController: UIViewController {
     var delegate: ClientDetailsControllerDelegate!
     static let sharedInstance = ClientDetailsController()
     @IBOutlet weak var loadingActivityIndicator: UIActivityIndicatorView!
     static func clientDetailsVC() -> ClientDetailsController {
         let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
         let clientDetailsVC = storyboard.instantiateViewController(withIdentifier: "ClientDetailsController") as! ClientDetailsController
         return clientDetailsVC
     }
    
     @IBAction func onClientDetailsSubmit(_ sender: UIButton) {
         let customerInfo = self.getCustomerInfoInput()
         self.delegate.onSubmitClientInfo(customerInfo: customerInfo!)
     }
    
     @IBAction func onBackAction(_ sender: UIButton) {
         self.navigationController?.popViewController(animated: true)
     }
    
     func getCustomerInfoInput() -> AFPCustomerInfo? {
         let appDelegate: AppDelegate? = UIApplication.shared.delegate as? AppDelegate
         var inputValues: AFPCustomerInfo? = appDelegate?.getPrefillTestInput()
         if inputValues == nil {
             inputValues = AFPCustomerInfo()
             inputValues?.email = "iossdk@affinipay.com"
             inputValues?.phone = "512-555-1212"
             inputValues?.reference = "iOS SDK sample app"
             inputValues?.city = "Austin"
             inputValues?.state = "Texas"
             inputValues?.country = "US"
             inputValues?.name = "Max Payne"
             inputValues?.address1 = "12"
             inputValues?.address2 = "xyz"
             inputValues?.postalCode = "12123"
         }
         return inputValues
     }
     func startLoading() {
         loadingActivityIndicator.startAnimating()
     }
     func stopLoading() {
         loadingActivityIndicator.stopAnimating()
     }
    }
    Implement ClientDetailsControllerDelegate where CardEntry instance was created.
    public class MainViewController: UIViewController, ClientDetailsControllerDelegate {
     // ...

    // MARK: - ClientDetailsControllerDelegate
     func onSubmitClientInfo(customerInfo: AFPCustomerInfo) {
         self.clientVC?.startLoading()
         let afpTokenizationCompleteParams = AFPTokenizationCompleteParams(customerInfo: customerInfo)
     self.afpCardEntryVC?.startTokenization(afpTokenizationCompleteParams!)
     }
    }

8: Trim the target architectures

The AffiniPay SDK includes the x86_64 and i386 architectures for ease of development on both simulators and iOS devices. You should remove these architectures before submitting your app for review. This can be done by adding a new build phase to your target in Xcode and running a script provided by the SDK:

  1. Open your project in Xcode.
  2. Navigate to the settings for the build target.
  3. Click the Build Phases tab.
  4. Add a new Run Script phase and paste the following into the script body. Adjust the FILE definition if your .framework file does not live in the same directory as your project file:

    FILE=$PROJECT_DIR/AffiniPaySDK.framework/Scripts/remove_unused_archs.sh
     source $FILE

Unused architectures will be removed from your build target as part of your build.

Orientation guidelines

The steps for configuring orientation are different depending on the navigation controller and view controllers you use.

Use AFPNavigationController with SDK-provided controllers

If you use AFPNavigationController for SDK-provided view controllers, the navigation controller will rotate the SDK-provided view controllers to their supported orientations.

Use AFPNavigationController with your own view controllers

If you push your own view controllers onto an instance of AFPNavigationController, you can choose to implement these functions to let AFPNavigationController orient your view controllers.

- (UIInterfaceOrientationMask)supportedInterfaceOrientationsForThisController{
    // Change this code to return the desired orientations
    if (isPhone()) return UIInterfaceOrientationMaskPortrait;
    return UIInterfaceOrientationMaskAll;
}

- (BOOL)shouldAutorotateNow {
    return YES;
}

Use your own navigation controller

If you push SDK-provided view controllers onto your own navigation controller, orient them based on the following values.

DeviceOrientations
iPhonePortrait
iPadAll