Skip to main content

iOS SDK

Integration options

Activity mode

The SmartPay Activity Mode provides you with a complete checkout screen. The user can navigate back to the previous activity by just pressing the back button.

Fragment mode

The SmartPay Fragment Mode allows you to integrate the SmartPay fragments directly into your app experience and gives you maximum control over the various checkout elements.

Add SmartPay SDK to your app

To integrate the SmartPay SDK into the other project, there is a necessity to perform a few basic tasks to prepare the project. This tutorial assumes your IDE for application development is Xcode.

First of all, add SmartPay.framework and KontoCloudSDK.bundle into your project.

To add SmartPay.framework copy it to your project's folder (or any other path which is suitable for you) and then in Xcode go to Project Editor's General tab of your target → Frameworks and Libraries section → tap + button, then click 'Add Other' and select SmartPay.framework at folder where you copied it. Another way is to simply drag and drop SmartPay.framework in Xcode's Project Navigator of your project. Make sure that in Project Editor's General tab in Frameworks and Libraries section there is 'Embed and Sign' option selected for SmartPay.framework.

To add KontoCloudSDK.bundle copy it to your project's folder (or any other path which is suitable for you) and then in Xcode go to Project Editor's Build Phases tab of your target → expand 'Copy Bundle Resources' section → tap + button, then click 'Add Other' and select KontoCloudSDK.bundle from there you copied it. Check 'Copy items if needed' option in dialog.

important

Important! Remove unused architectures

SmartPay SDK is a universal framework which allows to use it on real devices and in simulators. At the same time, AppStore doesn't allow to upload applications which contain universal frameworks (as it is not allowed to upload applications with unused architectures which are in this case iOS simulator ones). To avoid errors during the distribution of your application with SmartPay SDK to AppStore you should perform the following steps:

Go to your project target's build phases section and add new run script phase:

target build phases

Paste the following script as script body in appeared run script section:

APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"
# This script loops through the frameworks embedded in the application and
# removes unused architectures.
find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
do
FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"
EXTRACTED_ARCHS=()
for ARCH in $ARCHS
do
echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
done
echo "Merging extracted architectures: ${ARCHS}"
lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
rm "${EXTRACTED_ARCHS[@]}"
echo "Replacing original executable with thinned version"
rm "$FRAMEWORK_EXECUTABLE_PATH"
mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"
done

You should have the following result:

target build phases 2

Troubleshooting

  • If your app crashes during the attempt to display SmartPay UI with message "*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSBundle initWithURL:]: nil URL argument'" - most likely it means that KontoCloudSDK.bundle is not properly added. Please check if it is present in 'Copy Bundle Resources section' of Build Phases in your project's target.

  • If your app crashes at startup with message "dyld: Library not loaded: @rpath/SmartPay.framework/SmartPay Reason: image not found" - then please make sure that 'Embed and Sign' option is selected for SmartPay.framework in Frameworks and Libraries section in General tab of your project's target.

  • If you see error "No such module 'SmartPay'" when trying to import SmartPay - check path to SmartPay.framework file and Framework Search Paths of your project.

  • If you have error "App Store Connect Operation Error ERROR ITMS-90087: "Unsupported Architectures. The executable for SmartPayShowcase.app/Frameworks/SmartPay.framework contains unsupported architectures '[i386, x86_64]'." during the distribution of your application to AppStore - you need to exclude unused architectures from SmartPaySDK framework. Check previous section for more info (Important! Remove unused architectures).

error messages

Environment selection

Add import statement at the top of files where you want to use SmartPay SDK:

import SmartPay

SmartPay SDK has two environment options:

  • SmartPayEnvironment.TEST (correlates with SANDBOX environment)
  • SmartPayEnvironment.LIVE (correlates with PROD environment)

SmartPay SDK will use .TEST by default. If you want to use another environment - override smartPayOptions property of SmartPay:

import SmartPay

class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
SmartPay.shared.smartPayOptions = SmartPayOptions.init(smartPayMode: .LIVE)
return true
}
}

It is not mandatory to do it in AppDelegate, you can do it wherever it will be convenient for you. Just make sure that you override it before starting Activity or Fragment instances.

There is also no need to specifically initialize SmartPay. It will be automatically initialized when you will start using Activity or Fragment instances.

Complete payment in activity mode

Activity mode allows to present Checkout screens over current content as presentedViewController with its own navigation.

Get an instance of SmartPayCheckoutActivity

let smartPayCheckoutActivity = SmartPayCheckoutActivity.getActivityInstance(delegate: self)
// OR
let smartPayCheckoutActivity = SmartPayCheckoutActivity.getActivityInstance() // omit delegate parameter if you don't want to receive callbacks from Activity or if delegate is already set

Perform its startActivityForResult(viewController:, transactionId:, style:, completion:) function

smartPayCheckoutActivity.startActivityForResult(viewController: self, transactionId: transactionId, style: userStyle, completion: {})

where:

  • viewController - your view controller from which you want to present SmartPay Checkout screens (presentingViewController);
  • transactionId - string with transactionId;
  • style - custom user Style object for design (omit this parameter to use default style).

Receive activity result via SmartPayCheckoutActivityDelegate methods

extension ViewController: SmartPayCheckoutActivityDelegate {
func onActivityResult(result: SmartPayResult) {
// called when transaction succeeded or failed
}

func onActivityCancelled() {
// called when user cancelled Checkout by tapping on Back
}
}

SmartPayResult object will contain the following information about performed transaction:

  • type - SmartPayPaymentResultType (success, cancel or error);
  • transactionId - string with TransactionId;
  • transactionState - string with transaction state ("CAPTURED", "ERROR", "CAPTURE_PENDING");
  • message - string with any additional information about payment (usually is used for error messages).
SmartPayPaymentResultTypeDescription
SmartPayPaymentResultType.successSuccessful transaction execution
SmartPayPaymentResultType.errorTransaction has failed
SmartPayPaymentResultType.canceldeprecated and will be removed. onActivityCancelled() func should be used for cancelled case

Complete payment in fragment mode

Fragment mode allows user to show checkout screen(s) inside the specific view on the screen.

Get an instance of SmartPayCheckoutFragment

let smartPayCheckoutFragment = SmartPayCheckoutFragment.getFragmentInstance(containerEventListener: SmartPayContainerEventListener?, clickPaymentMethodListener: SmartPayClickPaymentMethod?, mandateListener: SmartPayMandateListener?)
// OR
let smartPayCheckoutFragment = SmartPayCheckoutFragment.getFragmentInstance() // omit any (or all) parameter if you don't want to receive specific callbacks from Fragment or if listener is already set

Add a container view on screen which will be used as a container for Checkout screens

Perform startFragment(step:, inContainer:, viewController:, transactionId:) function of smartPayCheckoutFragment instance

smartPayCheckoutFragment.startFragment(step: paymentProcessStep, inContainer: smartPayContainerView, viewController: self, transactionId: transactionId)

where:

  • step - case of enum SmartPayPaymentStep (optionsList, form) which indicates what step of Checkout process should be displayed in fragment;
  • inContainer - container view in which Checkout screen should be displayed;
  • viewController - UIViewController which will have a role of parent view controller for child Fragment view. Normally it should be a view controller which has container view;
  • transactionId - string with transactionId.
SmartPayPaymentStepDescription
optionsListFragment with list of available Payment Options
formFragment with Payment submit form (create or confirm)

Receive container (fragment) events

SmartPayContainerEventListenerDescription
smartPayFinished(withResult result: SmartPayResult)Payment process has ended with result
smartPayPaymentOptionStored()The payment option has been successfully stored. Return back to .optionsList fragment to see stored payment option
smartPayPaymentFormCancelled()Called when Payment Form was cancelled (closed) (for example, when cross button tapped on PayPal payment form)
smartPayPrepareFragment (fragmentStep: SmartPayPaymentStep)Called when fragment at specific step is preparing (loading or necessary data is not selected/provided). Means that payment can not be continued, could be used for disabling/renaming buttons in user's interface
smartPayReadyFragment (fragmentStep: SmartPayPaymentStep)Called when fragment at specific step is ready. Means that user can interact with it and continue payment

Example of SmartPayContainerEventListener usage:

extension ViewController: SmartPayContainerEventListener {
func smartPayFinished(withResult result: SmartPayResult) {
// show result
}

func smartPayPaymentOptionStored() {
// open screen with .optionsList fragment
}

func smartPayPaymentFormCancelled() {
// open screen with .optionsList fragment
}

func smartPayPrepareFragment(fragmentStep: SmartPayPaymentStep) {
// for example: show loading if .optionsList fragment is on the screen
guard fragmentStep == .optionsList else { return }
// show loading UI
}

func smartPayReadyFragment(fragmentStep: SmartPayPaymentStep) {
// for example: hide loading if .optionsList fragment is on the screen
guard fragmentStep == .optionsList else { return }
// hide loading UI
}
}

Receive SmartPayClickPaymentMethod events

SmartPayClickPaymentMethodDescription
smartPayClickCreateStoredPaymentMethod()Called when the end-customer selects payment option which will lead to process of creation of new stored payment
smartPayClickStoredPaymentMethod()Called when the end-customer selects already stored payment option or default payment option was selected automatically
smartPayClickGuestPaymentMethod()Called when the end-customer selects payment option that hasn't been already stored

Example of SmartPayClickPaymentMethod events usage:

extension ViewController: SmartPayClickPaymentMethod {
func smartPayClickCreateStoredPaymentMethod() {
// open next screen which contains .form fragment
}

func smartPayClickStoredPaymentMethod() {
// show PaymentConfirmation screen
}

func smartPayClickGuestPaymentMethod() {
// show PaymentConfirmation screen
}
}

receive SmartPayMandateListener events

While the end-customer pays with SEPA payment option, the mandate with terms and conditions is shown. SmartPayMandateListener is responsible for notifying when end-customer clicked on Accept or Cancel in mandate. If the end-customer clicks 'Cancel' it is necessary to return to the payment selection screen.

SmartPayMandateListenerDescription
smartPayMandateCancel()Called when user clicked Cancel button in mandate
smartPayMandateAccept()Called when user clicked Accept button in mandate

Example of SmartPayMandateListener events usage:

extension ViewController: SmartPayMandateListener {
func smartPayMandateCancel() {
// go back to screen with .optionsList fragment
}

func smartPayMandateAccept() {
// do something
}
}

Override SmartPay continue button

It is possible to override selector, title and visibility of the default SmartPay continue button.

Override title

Use this method to change the title of default continue button. To change title - get current instance of SmartPayCheckoutActivity or SmartPayCheckoutFragment and call its setTextConfirmButton(title:, forStep:) function:

smartPayCheckoutActivity.setTextConfirmButton(title: "Custom title", forStep: .form)        // for activity mode
// OR
smartPayCheckoutFragment.setTextConfirmButton(title: "Custom title", forStep: .form) // for fragment mode

Override visibility

Use this method to hide default buttons if you want to use your own buttons from your own screens. To hide default button - get current instance of SmartPayCheckoutActivity or SmartPayCheckoutFragment and call its setVisibilityConfirmButton(visibility:, forStep:) function:

smartPayCheckoutActivity.setVisibilityConfirmButton(visibility: false, forStep: .optionsList)   // hide continue button for options list in activity mode
// OR
smartPayCheckoutFragment.setVisibilityConfirmButton(visibility: false, forStep: .optionsList) // hide continue button for options list in fragment mode

Override selector

Use this method if you want the default button to be visible but to perform some specific action when it is clicked. To override selector - get current instance of SmartPayCheckoutActivity or SmartPayCheckoutFragment and call its setOnClickConfirmButton(forStep: .optionsList, onClick:) function with onClick closure as new button selector and specific payment step for which this override should be applied:

// override continue button selector for Activity
smartPayCheckoutActivity.setOnClickConfirmButton(forStep: .optionsList, onClick: { [weak self] in
// perform any custom actions
})

// OR

// override continue button selector for Fragment
smartPayCheckoutFragment.setOnClickConfirmButton(forStep: .optionsList, onClick: { [weak self] in
// perform any custom actions
})

Be aware to not use strong pointers in closure to prevent retain cycles.

Override methods could be called independently of current state of Activity screens / Fragments. If override method was called before screen was created and displayed on the screen - it will automatically be applied when screen will be ready.

Continue manually

When you override default Smart Pay SDK continue button you may eventually need to notify SDK that it is necessary to continue payment process. For example after selecting Payment option or after filling all data in payment form.

For SmartPayCheckoutActivity use func continueManually() -> SmartPayContinueManuallyResult. Activity mode has automatic navigation so it will continue from current step.

For SmartPayCheckoutFragment use function func continueManually(forStep:) -> SmartPayContinueManuallyResult. For Fragment mode you need to specify what exact step should be continued.

Example of usage:

@IBAction func userConfirmButtonClicked(_ sender: Any) {       
let continueResult = self.smartPayCheckoutFragment.continueManually(forStep: .optionsList)
if continueResult == .willContinueToNextStep {
openContainerSmartPayViewController(withTransactionId: transactionId!)
}

Function continueManually() will return value of SmartPayContinueManuallyResult (it can be omitted if you don't need it).

SmartPayContinueManuallyResult casesDescription
.willFinishPaymentMeans that current step is last in payment process and no more screens should be presented
.willContinueToNextStepMeans that there is a next step (.form) in payment process (you may want to open screen with .form Fragment in this case)
.errorPayment could not be continued (for example if you try to use continueManually() for .optionsList while PaymentOption is not selected).

Selected payment option view

There is SmartPayPaymentView implemented to show already selected payment option by the end-customer to pay for the order. This element can be designed separately from the main theme. To have everything working correctly this element must be shown only after the end-customer has selected a payment option to pay on the SmartPayCheckoutFragment screen.

Examples:

payment option view1 payment option view2

SmartPayPaymentView can be added programmatically or in xib/storyboard.

Example of adding SmartPayPaymentView programmatically:

smartPayPaymentView = SmartPayPaymentView()
smartPayPaymentView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(smartPayPaymentView)

smartPayPaymentView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 20).isActive = true
smartPayPaymentView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 20).isActive = true
smartPayPaymentView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -20).isActive = true
smartPayPaymentView.heightAnchor.constraint(equalToConstant: 65).isActive = true
self.view.layoutIfNeeded()

To add SmartPayPaymentView in xib/storyboard - place view element in your view/viewController, select it, open identity inspector and set view class to 'SmartPayPaymentView' and module to 'SmartPay' in Custom Class section:

identity inspector

To update added SmartPayPaymentView (for example if it was added before selecting payment option) - use function updateView() of SmartPayPaymentView instance. Parameter userStyle should be omitted if you just want update selected payment option and don't want to update design:

smartPayPaymentView.updateView()

If you want to update SmartPayPaymentView design - pass userStyle property in updateView() function:

let paymentViewCustomStyle = PaymentViewStyle(paymentViewStylePrimaryTextColor: ... , ... , paymentViewStyleRadiusSize: ...)
smartPayPaymentView.updateView(withUserStyle: paymentViewCustomStyle)

Apply own styling (both integrations)

Smart Pay allows some design customization. To apply custom design you need to pass initialized Style object as a parameter in startActivityForResult(viewController:, transactionId:, style:, completion:) function of SmartPayCheckoutActivity or in startFragment(step:, inContainer:, viewController:, transactionId:, style:, completion:) function of SmartPayCheckoutFragment depending of what mode you are using.

Style object includes styles for all customizable elements of the Smart Pay SDK:

public class Style {
var globalStyle: GlobalStyle?
var confirmButtonStyle: ConfirmButtonStyle?
var paymentOptionStyle: PaymentOptionStyle?
var retryButtonStyle: RetryButtonStyle?
var paymentViewStyle: PaymentViewStyle?
}

GlobalStyle object includes some basic options for application appearance:

GlobalStyle propertyDefault valueDescription
var smartPayColorBackgroundFragment: UIColorUIColor.whiteBackground color for fragments
var smartPayColorNavigationBarBackground: UIColor#0C384EBackground color for navigation bar
var smartPayColorNavigationBarText: UIColorUIColor.whiteColor for texts and buttons in navigation bar
var smartPayStatusBarStyle: UIBarStyleUIBarStyle.blackStyle of status bar (for light or dark content)

ConfirmButtonStyle determines the appearance of default 'Continue' button on payment steps screens:

ConfirmButtonStyle propertyDefault valueDescription
var smartPayColorButtonConfirm: UIColor#0C384EBackground color for normal state of button
var smartPayColorButtonConfirmSelected: UIColor#0C384EBackground color for selected state of button
var smartPayColorButtonConfirmRipple: UIColor#0C384EColor of ripple animation effect (#not implemented yet)
var smartPayColorButtonConfirmCorners: Float0.0Corner radius
var smartPayColorButtonConfirmText: UIColorUIColor.whiteColor of title text for normal state of button
var smartPayColorButtonConfirmSelectedText: UIColorUIColor.whiteColor of title text for selected state of button
var smartPayColorButtonConfirmDeactivatedText: UIColor#0C384EColor of title text for disabled state of button
var smartPayColorButtonConfirmDeactivated: UIColor#0C384EBackground color for disabled state of button

PaymentOptionStyle object handles the design for Payment Options cells on Payment Options List screen (.optionList):

PaymentOptionStyle propertyDefault valueDescription
var smartPayColorPrimaryTextColor: UIColor#4D5356Color for primary text in payment option cell
var smartPayColorSecondaryTextColor: UIColor#A9ADB2Color for secondary text in payment option cell
var smartPaySizePrimaryText: Float12Size of primary text in payment option cell
var smartPaySizeSecondaryText: Float12Size of secondary text in payment option cell
var smartPayColorSelectedText: UIColor-#not used currently
var smartPayColorShowAll: UIColor#194663Text color for labels: 'Show all', 'Show less', 'OR'. Text color for the arrow.
var smartPayColorImageShowBackground: UIColor#87E1ECArrow background color; 'Show all/less' background color
var smartPayColorSelectedCardImageTint: UIColor#5DCAA2Check mark color
var smartPayColorCardBackground: UIColorUIColor.whitePayment option cell background color
var smartPayColorSelectedCardBackground: UIColorUIColor.whiteSelected payment option cell background color
var smartPayColorCardBorder: UIColor#A9ADB2Payment option cell border color
var smartPayColorSelectedCardBorder: UIColor#071E2BSelected payment option cell color
var smartPayColorCardRipple: UIColor#A9ADB2Ripple effect color. Ripple effect is shown after clicking the payment option (not implemented yet)
var smartPaySizeCardBorder: Float1.0Payment option cell border size
var smartPaySizeCardRadius: Float0.0Payment option cell border curving size

RetryButtonStyle contains properties for Retry button customization (error view):

RetryButtonStyle propertyDefault valueDescription
var smartPayRetryButtonBorder: UIColorUIColor.blackColor for border for normal state of button
var smartPaySelectedRetryButtonBorder: UIColorUIColor.blackColor for border for selected state of button
var smartPayRetryButtonBackground: UIColorUIColor.whiteBackground color for normal state of button
var smartPaySelectedRetryButtonBackground: UIColorUIColor.whiteBackground color for selected state of button
var smartPayRetryButtonSizeBorder: Float0.5Size of border
var smartPayRetryButtonSizeRadius: Float0.0Corner radius size

PaymentViewStyle determines design for SmartPayPaymentView:

PaymentViewStyle propertyDefault valueDescription
var paymentViewStylePrimaryTextColor: UIColor#4D5356Color of primary text
var paymentViewStyleSecondaryTextColor: UIColor#A9ADB2Color of secondary text
var paymentViewStyleSizePrimaryText: Float12Size of primary text
var paymentViewStyleSizeSecondaryText: Float12Size of secondary text
var paymentViewStyleBackgroundColor: UIColorUIColor.whiteBackground color
var paymentViewStyleBorderColor: UIColorUIColor.darkGrayColor for border
var paymentViewStyleBorderSize: Float1.0Size of border
var paymentViewStyleRadiusSize: Float5.0Corner radius size

All properties in style objects are nilable which means that they can be omitted during the style object initialization. If property is omitted - SDK will use default value for it. For example, if you want to set custom border color for SmartPayPaymentView while preserving all other parameters by default - you can initialize PaymentViewStyle in the following way:

let paymentViewStyle = PaymentViewStyle(paymentViewStyleBorderColor: UIColor.green)     // This will create PaymentViewStyle with green border color and all other parameters with default values

You can also omit some styles in Style object initialization in the same way. Example of creating Style object which will change appearance only for navigation bar background color and for confirm button background color and corners and use it for Activity mode:

let globalStyle = GlobalStyle.init(smartPayColorNavigationBarBackground: UIColor.systemOrange)
let confirmButtonStyle = ConfirmButtonStyle.init(smartPayColorButtonConfirm: UIColor.systemOrange,
smartPayColorButtonConfirmCorners: 7)

let customUserStyle = Style(globalStyle: globalStyle, confirmButtonStyle: confirmButtonStyle)
smartPayCheckoutActivity.startActivityForResult(viewController: self, transactionId: transactionId, style: userStyle)