Using the Mobile SDK

Check out our mobile SDK.

This guide shows how to use the various features. The code samples are in Swift for iOS and Java and Kotlin for Android.

1. Setup the heidelpay instance

When using the Unzer SDK you communicate with the Unzer server through an instance of the type heidelpay. The first thing to do is to setup this instance with your Public Key.

  1. Create a PublicKey object
  2. Call the class method setup on the heidelpay class, provide the public key object and a completion handler which has two parameter.

During setup the public key is verified with the Unzer backend and the available Payment Methods (associated with that public key) are retrieved. As soon as the setup completed, your completion handler is called. When setup succeeded the completion handler is called with a heidelpay instance as the first parameter and nil for the second parameter. In case of an error the first parameter is nil and the second parameter holds an error object of type heidelpayError. See the section below for a full list of all errors you have to deal with.

It’s guaranteed that only one parameter of the completion handler is nil.

Keep a reference to the returned heidelpay instance for further usage.

// some place to hold the reference
var heidelpay: Heidelpay? 

// 1. create a PublicKey object
let publicKey = PublicKey("s-pub-xxxxxxxxxx")

// 2. setup the HeidelPay instance
Heidelpay.setup(publicKey: publicKey) { (heidelPayInstance, error) in 
  if let heidelPayInstance = heidelpayInstance {
    // setup succeeded. store reference
    heidelpay = heidelpayInstance
  } else if let error = eror{
    // handle the error 
  }
}
// some place to hold the reference (e.g. in your implementation of
// android.app.Application)
Heidelpay heidelpay;
  
// 1. create a PublicKey object
PublicKey key = new PublicKey("s-pub-xxxxxxxxxx");

// 2. setup the HeidelPay instance
Heidelpay.Companion.setup(key, new Function2<Heidelpay, HeidelpayError, Unit>() {
  @Override
  public Unit invoke(Heidelpay heidelpay, HeidelpayError heidelpayError) {
    if(heidelpayError != null) {
      //handle error
    } else {
      // setup succeeded. store reference
      Application.heidelpay = heidelpay;      
    }
    return null;
  }
});
// some place to hold the reference (e.g. in your implementation of 
// android.app.Application)
var heidelpay: Heidelpay? = null
  
// 1. create a PublicKey object
val key = PublicKey("s-pub-xxxxxxxxxx")

// 2. setup the HeidelPay instance
Heidelpay.setup(key, completion = { heidelpay, error ->
  if(error != null) {
    //handle error
  } else {
    // setup succeeded. store reference
    this.heidelpay = heidelpay
  }
})

For your checkout user interface it’s important to check which payment methods are available. The Unzer shared instance provides a property paymentMethods which has an array of PaymentMethod elements.

Payment Methods in SDK
Not all payment methods that are implemented by the mobile SDK may be available at runtime. During setup the list of currently available payment methods is retrieved by the server. It’s therefore important to always use the paymentMethods property to check which payment methods the customer can choose from.
// check if the card payment method is available
if heidelpay.paymentMethods.contains(.card) {
    
}
// check if the card payment method is available
if (heidelpay.getPaymentMethods().contains(PaymentMethod.Card)) {
}
// check if the card payment method is available
if (heidelpay!!.paymentMethods.contains(PaymentMethod.Card)) {
}

2. Error Handling

In case of an error during setup or payment type creation you receive detailed information about the problem that occurred. All errors are of the type heidelpayError.

The following errors are technical errors and not related to the customer data:

  • no internet connection: The Unzer server could not be reached and it seems that there is no internet connection. You should handle this error and show a message to the customer.
  • not authorized: The Unzer server rejected the request or the provided public key is not valid. This error is typically the result of a configuration error by providing an outdated public key.
  • general processing error: A technical problem occurred during communication with the server. This might be a temporary problem on the backend side.

In case of an error that’s related to customer data or an invalid state on the Unzer backend a server error object is returned which contains detail information about the error itself. The details object is of type ServerErrorDetails and contains a localised message which can be displayed to the customer.

The server might return more that one error object. The additionalErrorDetails array gives you access to these error objects.

switch error {        
  case .noInternetConnection:
    // the server could not be reached because there seems to 
    // be no internet connection

  case .notAuthorized:
    // the provided key is not authorized to use the HeidelPay service

  case .generalProcessingError:
     // the request failed because of a technical issue. 
     // try again at a later time or contact technical
     // support if the error persists
     
  case let .serverError(details):
    // the server reported an error
    
    /// get the message which can displayed to the customer
    let customerMessage = details.customerMessage
    
    /// internal message for logging or further error handling
    let serverErrorCode = details.code 
    let internalMessage = details.merchantMessage
}
if(heidelpayError instanceof HeidelpayError.NoInternetConnection) {
  // the server could not be reached because there seems to
  // be no internet connection
} else if(heidelpayError instanceof HeidelpayError.GeneralProcessingError) {
  // the request failed because of a technical issue.
  // try again at a later time or contact technical
  // support if the error persists
} else if(heidelpayError instanceof HeidelpayError.NotAuthorized) {
  // the provided key is not authorized to use the HeidelPay service
} else if(heidelpayError instanceof HeidelpayError.ServerError) {
  // the server reported an error
  
  HeidelpayError.ServerError error = (HeidelpayError.ServerError) heidelpayError;
  // get the message which can displayed to the customer
  String customerMessage = error.getDetails().getCustomerMessage();
  String serverErrorCode = error.getDetails().getCode();
  
  // internal message for logging or further error handling
  String internalMessage = error.getDetails().getMerchantMessage();
}
when(error) {
  is HeidelpayError.NoInternetConnection -> {
    // the server could not be reached because there seems to
    // be no internet connection
  }
  is HeidelpayError.GeneralProcessingError -> {
    // the request failed because of a technical issue.
    // try again at a later time or contact technical
    // support if the error persists
  }
  is HeidelpayError.NotAuthorized -> {
    // the provided key is not authorized to use the HeidelPay service
  }
  is HeidelpayError.ServerError -> {
    // the server reported an error
    
    // get the message which can displayed to the customer
    val customerMessage = error.details.customerMessage
    val serverErrorCode = error.details.code
    
    // internal message for logging or further error handling
    val internalMessage = error.details.merchantMessage
  }
}

Create a Payment Type

For each payment type supported by the SDK the heidelpay instance provides a createPayment... method.

The parameters of the create payment method depends on the payment type (e.g. card requires a card number, cvc and exipiry date while iDEAL requires a BIC). All create methods require that you provide a completion block as last parameter. Similar to the setup completion block the create completion block will be called after the request has been processed. In case of success the first parameter holds a PaymentType instance for the newly create payment type. In case of an error the first parameter is nil and the second parameter holds an error element. It’s guaranteed that only one of the two parameter is set and the other one is nil.

The PaymentType element contains the paymentId. With the payment id you are able to initiate a charge on your backend.

heidelpay.createPaymentCard(number: "4444333322221111",
      cvc: "123", expiryDate: "04/25") { (paymentType, error) in

  if let error = error {
    // handle the error 
  } else if let paymentType = paymentType {
    // payment type holds the payment id
    let paymentId = paymentType.paymentId
    
    // 1. transfer the payment id to your server
    // 2. create a charge with the payment id on your server
  }       
}
HeidelpayCreatePaymentTypeExtensionsKt.createPaymentCard(
   heidelpay, 
   "4444333322221111", 
   "123", 
   "04/25", 
   new Function2<PaymentType, Object, Unit>() {
      @Override
      public Unit invoke(PaymentType paymentType, Object error) {
        if (error != null) {
          // handle the error
        } else if (paymentType != null) {
          // payment type holds the payment id
          String paymentId = paymentType.getPaymentId();

          // 1. transfer the payment id to your server
          // 2. create a charge with the payment id on your server
      }
    return null;
  }
});
heidelpay!!.createPaymentCard(
  number = "4444333322221111", 
  cvc =  "123", 
  expiryDate =  "04/25", 
  completion = { paymentType, error ->
    if (error != null) {
      // handle the error
    } else if (paymentType != null) {
      // payment type holds the payment id
      val paymentId = paymentType.paymentId
  
      // 1. transfer the payment id to your server
      // 2. create a charge with the payment id on your server
    }
}

The Payment Type Id is the required element to trigger a charge on the server side. The following diagram shows a typical workflow.

mobile-checkout-normal.png

  1. The user choose a payment method (e.g. credit card) and you presented an UI to get the payment information.
  2. The gathered information are used to create a payment type (see above) and on success you get a PaymentType element which holds a payment type id
  3. Send the payment type id to your server.
  4. You may store the id for a potential future use (you may trigger another purchase with that payment id). This step is optional!
  5. Trigger a charge with the payment id from your server (the Unzer private key is needed) and return the result to your app.

4. Handling Charge Redirects

Some payment types require the customer to confirm a charge. E.g. a MasterCard with 3DS might require the user to enter a unique code. For these cases you have to handle Charge Redirects in your mobile app.

mobile-checkout-with-redirect.png

  1. The user triggers a checkout for a particular payment type on your mobile app.
  2. You transfer the payment id to your server.
  3. On your server you trigger the charge with the payment id.
  4. Unzer responds with a pending state and provides you with a Redirect and Return URL.
  5. You send that Redirect and Return URL back to your app.
  6. Instantiate the heidelpayFinishCharge (ViewController on iOS and Fragment on Android) and set the Redirect and Return URL and a delegate/listener and present that UI in your app.
  7. The heidelpayFinishCharge`` UI displays the confirmation UI to the customer and informs the Unzer backend when the process has completed. As soon as the user completes or cancels the process your delegate is called and you shall dismiss the heidelpayFinishCharge``` UI again.
  8. Call your server to get the status of the charge.
let redirectURL:URL // as received from your backend
let returnURL:URL   // as received from your backend

let finishChargeVC = 
    HeidelpayFinishChargeViewController(delegate: yourViewController, 
                                      redirectURL: redirectURL, 
                                      returnURL: returnURL)

// present the view controller
// e.g. as modal
let navVC = UINavigationController(rootViewController: finishChargeVC)
yourVC.present(navVC, animated: true, completion: nil)

// implement the HeidelpayFinishChargeViewControllerDelegate 
extension YourViewController: HeidelpayFinishChargeViewControllerDelegate {
    
    func heidelpayChargeViewControllerDidFinish(canceledByUser: Bool,
           heidelpayController: HeidelpayFinishChargeViewController) {
        
        // close the finish charge view and
        // retrieve the status of the payment from your backend
    
    }
}
// implement the interface HeidelpayFinishChargeFragmentListener

String redirectUrl // as received from your backend
String returnUrl   // as received from your backend

HeidelpayFinishChargeFragment fragment = new HeidelpayFinishChargeFragment();
fragment.setArguments(new Bundle());
 
fragment.getArguments().putString(
  HeidelpayFinishChargeFragment.Companion.getEXTRA_REDIRECT_URL(), redirectUrl);
 
fragment.getArguments().putString(
  HeidelpayFinishChargeFragment.Companion.getEXTRA_RETURN_URL(), returnUrl);

// show this fragment the way you like - note that the displaying Activity 
// has to implement HeidelpayFinishChargeFragmentListener

class YourActivity implements HeidelpayFinishChargeFragmentListener {
    @Override
    public void heidelpayChargeFragmentDidFinish(boolean b) {
        // close the finish charge view and
        // retrieve the status of the payment from your backend
    }
}
// implement the interface HeidelpayFinishChargeFragmentListener

val redirectUrl // as received from your backend
val returnUrl   // as received from your backend

val fragment = HeidelpayFinishChargeFragment()
fragment.arguments = Bundle()

fragment.arguments?.
  putString(HeidelpayFinishChargeFragment.EXTRA_REDIRECT_URL, redirectUrl)

fragment.arguments?.
  putString(HeidelpayFinishChargeFragment.EXTRA_RETURN_URL, returnUrl)

// show this fragment the way you like - note that the displaying Activity 
// has to implement HeidelpayFinishChargeFragmentListener

class YourActivity: ... HeidelpayFinishChargeFragmentListener {
  override fun heidelpayChargeFragmentDidFinish(canceledByUser: Boolean) {
    
     // close the finish charge view and
     // retrieve the status of the payment from your backend
    
  }
}

5. Using the UI Components

The Unzer SDK ships with UI components that make it convenient for your customer to enter various payment information.

There are UI components for

  • Credit Card Number
  • CVC Number
  • Expiry Date
  • IBAN

These components do basic verification like length and allowed character, set the correct keyboard type and for Credit Card Number and IBAN also verify the checksum. In case of Credit Card Number and IBAN the text is also grouped for easier reading.

// If you build your UI with Interface Builder just
// add UITextField components to your UI and set the 
// "Custom Class" to the desired UI component

// in code you can use the components like this
let cardNumberField = CardNumberTextField(frame: .zero)
let cardCvcField    = CardCvvTextField(frame: .zero)
let expiryField     = CardExpiryTextField(frame: .zero)
let ibanField       = IBANTextField(frame: .zero)

// add to your view as any other UIView
view.addSubview(cardNumberField)

// create a card payment out of the data
if let cardInput = cardNumberField.userInput, 
   let cvcInput = cardCvcField.userInput,
   let expiryInput = expiryField.userInput {

  if cardInput.valid, cvcInput.valid, expiryInput.valid {
    heidelpay.createPaymentCard(number: cardInput.normalizedCardNumber,
                                cvc: cvcInput.cvc
                                expiryDate: expiryField.expiryDate) {
                                (paymentType, error) in
       // continue work with created payment type or
       // handle error
    }
  }
}

// create a Sepa Direct Payment out of the data
if let ibanInput = ibanField.userInput, ibanInput.valid {
  heidelpay.createPaymentSepaDirect(iban: ibanInput.iban) {
                                   (paymentType, error) in
     // continue work with created payment type or
     // handle error
  }
}
//Configure your UI as usual in your layout xml file (see XML tab)
CreditCardNumberEditText creditCardNumberEditText = // bind this view as usual
CvvEditText cvcEditText = // bind this view as usual
CardExpiryEditText expiryEditText = // bind this view as usual

CreditCardInput cardInput = creditCardNumberEditText.getUserInput();
CvvInput cvcInput = cvcEditText.getUserInput();
CardExpiryInput expiryInput= expiryEditText.getUserInput();

if (cardInput.getValid() && cvcInput.getValid() && expiryInput.getValid()) {
  
  HeidelpayCreatePaymentTypeExtensionsKt.createPaymentCard(
    heidelpay, 
    cardInput.getCreditCardNumber(), 
    cvcInput.getCvv(), 
    expiryInput.getExpiryDate(), 
    new Function2<PaymentType, Object, Unit>() {
      @Override
      public Unit invoke(PaymentType paymentType, Object error) {
        // continue work with created payment type or
        // handle error
        return null;
      }
  });
}
//Configure your UI as usual in your layout xml file (see tab 'Android Layout')

val creditCardNumberEditText : CreditCardNumberEditText = // bind this view as usual
val cvcEditText : CvvEditText = // bind this view as usual
val expiryEditText : CardExpiryEditText = // bind this view as usual
        
val cardInput = creditCardNumberEditText.userInput
val cvcInput = cvcEditText.userInput
val expiryInput= expiryEditText.userInput
        
if (cardInput!!.valid && cvcInput!!.valid && expiryInput!!.valid) {
  
  heidelpay.createPaymentCard(
    number = cardInput.creditCardNumber, 
    cvc = cvcInput.cvv, 
    expiryDate = expiryInput.expiryDate, 
    completion = { paymentType, error ->
    // continue work with created payment type or
    // handle error
  })
}
<!--Configure your UI as usual in your layout xml file -->
<com.heidelpay.android.ui.IBANEditText
  android:id="@+id/ibanEditText"
  android:layout_width="match_parent"
  android:layout_height="40dp" />
        
<com.heidelpay.android.ui.CreditCardNumberEditText
  android:id="@+id/creditCardNumberEditText"
  android:layout_width="wrap_content"
  android:layout_height="40dp"
  android:layout_alignParentLeft="true"
  android:layout_toLeftOf="@+id/cardExpiryEditText" />

<com.heidelpay.android.ui.CardExpiryEditText
  android:id="@+id/cardExpiryEditText"
  android:layout_width="80dp"
  android:layout_height="40dp"
  android:layout_toLeftOf="@+id/cvvEditText" />

<com.heidelpay.android.ui.CvvEditText
  android:id="@+id/cvvEditText"
  android:layout_width="80dp"
  android:layout_height="40dp"
  android:layout_alignParentRight="true" />

For iOS the colours for text and errors as well as fonts of the UI components can be adjusted through the heidelpayTheme shared instance.

For Android colours and errors can be adjusted by the usual Android mechanism (e.g. styles.xml).

// change the primary text color of the fields
HeidelpayTheme.sharedInstance.primaryForegroundColor = .black

// set the font used by the fields
HeidelpayTheme.sharedInstance.font = UIFont.systemFont(ofSize: 17, 
                                                       weight: .regular)

// see the SDK API documentation for the full list of properties
<!-- Here the textColor is changed to @color/darkText.
Additionally all UI components support a custom attribute errorTextColor 
to set the text color when the validation of the input field fails. Here errorTextColor is set to @color/errorColor
-->
<com.heidelpay.android.ui.IBANEditText
  android:id="@+id/ibanEditText"
  android:layout_width="match_parent"
  android:layout_height="40dp"
  android:textColor="@color/darkText"
  app:errorTextColor="@color/errorColor"
/>