iOS/Swift and Android SDKs reference

The easiest way to start accepting payments on mobile devices using ProcessOut is to use the ProcessOut mobile SDKs. The SDKs will take care of tokenizing your customers’ card numbers, so that you can send those generated tokens to your backend and process payments ↗.


Set up the mobile SDK in your app

Let’s first make sure the ProcessOut SDK is installed and set up on your mobile application.

// The ProcessOut iOS SDK is available on *iOS 8.0+*, 
// through http://cocoapods.org/.
//
// To install it,  simply add the following line to your Podfile:
//   pod 'ProcessOut'
// And then run `pod install`.

// Once installed, in your AppDelegate, configure the ProcessOut iOS SDK:
ProcessOut.Setup(projectId: "proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x")
// The ProcessOut Android SDK is available on *Android SDK 14+*, 
// through Jitpack.
//
// To install it,  simply add the following line to
// your build file, under the repositories section:
//   maven { url 'https://jitpack.io' }
// as well as the following dependency in your build.gradle file:
//   compile 'com.github.processout:processout-android:2.+'

// You’ll then be able to import Processout in your code base and 
// configure the SDK with your ProcessOut API credentials.
final ProcessOut client = new ProcessOut(
    this, 
    "proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x"
);

Tokenize a card

Once all set up, you’ll be able to tokenize the card numbers. The returned token is a simple string.

// First create a card object containing the card details
let card = ProcessOut.Card(cardNumber: "4242424242424242", expMonth: 11, expYear: 19, cvc: nil, name: "NAME")

// And then send the card data to ProcessOut to tokenize
ProcessOut.Tokenize(card: card, metadata: [:], completion: {(token, error) -> Void in
    if error != nil {
        switch error! {
        case .BadRequest(let message, let code):
            print(message, code)
        case .InternalError:
            print("An internal error occured")
        case .MissingProjectId:
            print("Check your app delegate file")
        case .NetworkError:
            print("Request could not go through")
        }
    } else {
        // Send token to your backend to charge the customer
        print(token!)
    }
})
// First create a card object containing the card details
Card card = new Card("Jeremy lejoux", "4242424242424242", 11, 19, "123");

// And then send the card data to ProcessOut to tokenize
client.tokenize(card, new TokenCallback() {
    @Override
    public void onSuccess(String token) {
        // Send the card token to your backend for charging
    }

    @Override
    public void onError(Exception error) {
        Log.e("ProcessOut", error.toString());
    }
});

3DS2 handlers

3DS2 handlers are native elements that allow you to implement 3DS2 in your application while giving the best experience to your customers.

1— The test handler

The test handler is very simple: it simply emulates the 3DS authentication flow without having the make actual calls to ACS servers (servers of the banks of your customers used to process authentications).

The ProcessOut SDKs provide factories to build the 3DS2 test handler:

let handler = ProcessOut.createThreeDSTestHandler(viewController: self, completion: { (invoiceId, error) in
    if invoiceId != nil {
        // The authentication was done successfully, you can send
        // back the invoice ID to your backend to validate the payment
        print("invoice: " + invoicecId)
    } else {
        // An error occurred
        print(error)
    }
})
final ThreeDSHandler handler = ProcessOut.createDefaultTestHandler(
    MainActivity.this,
    new ProcessOut.ThreeDSHandlerTestCallback() {
        @Override
        public void onSuccess(String invoiceId) {
            // The authentication was done successfully, you can send
            // back the invoice ID to your backend to validate the payment
            Log.d("PROCESSOUT", "invoice: " + invoiceId);
        }

        @Override
        public void onError(Exception error) {
            // An error occurred
            Log.e("PROCESSOUT", error.toString());
        }
    }
);

The test handler can basically be used when integrating using ProcessOut’s sandbox, and can also be used to mock tests in your application.

Once you chose to deploy your mobile application to production, you can then start using the official 3DS SDKs from your 3DS Server partners.

2— The handler with the production 3DS SDK

As mentioned previously, the ProcessOut 3DS2 SDK works in a “bring your own SDK” manner. Most 3DS Server providers/partners will provide you with their own certified SDKs, which might also come with specific features you’d like to integrate.

To help in the process, you can find below the 3DS SDKs we officially support:

2.1— Adyen

The below code implements the ProcessOut 3DS2 SDK using Adyen’s certified SDK.

import ProcessOut
import Adyen3DS2

class AdyenThreeDSHandler: ThreeDSHandler {
    var transaction: ADYTransaction?

    public func doPresentWebView(webView: ProcessOutWebView) {
        // Called when a web challenge is required. Present 
        // the webview to the user
    }

    func doFingerprint(directoryServerData: DirectoryServerData, completion: @escaping (ThreeDSFingerprintResponse) -> Void) {
        let parameters = ADYServiceParameters()
        parameters.directoryServerIdentifier = directoryServerData.directoryServerID
        parameters.directoryServerPublicKey = directoryServerData.directoryServerPublicKey

        ADYService.service(with: parameters, appearanceConfiguration: nil, completionHandler: {(service: ADYService) -> Void
            in
            do {
                self.transaction = try service.transaction(withMessageVersion: nil)
                    let authReqParams = self.transaction!.authenticationRequestParameters
                    if let sdkEphemPubKeyData = authReqParams.sdkEphemeralPublicKey.data(using: .utf8) {
                        let sdkEphemPubKey = try JSONDecoder().decode(ThreeDSFingerprintResponse.SDKEphemPubKey.self, from: sdkEphemPubKeyData)
                        let fingerprintResponse = ThreeDSFingerprintResponse(
                            sdkEncData: authReqParams.deviceInformation,
                            sdkAppID: authReqParams.sdkApplicationIdentifier,
                            sdkEphemPubKey: sdkEphemPubKey,
                            sdkReferenceNumber: authReqParams.sdkReferenceNumber,
                            sdkTransID: authReqParams.sdkTransactionIdentifier
                        )

                        completion(fingerprintResponse)
                    } else {
                        print("Could not encode sdkEphem")
                    }
            } catch {
                print(error)
            }
        })
    }

    func doChallenge(authentificationData: AuthentificationChallengeData, completion: @escaping (Bool) -> Void) {
        let challengeParameters = ADYChallengeParameters(
            serverTransactionIdentifier: authentificationData.threeDSServerTransID, 
            acsTransactionIdentifier: authentificationData.acsTransID, 
            acsReferenceNumber: authentificationData.acsReferenceNumber, 
            acsSignedContent: authentificationData.acsSignedContent
        )

        transaction?.performChallenge(with: challengeParameters, completionHandler: { (result, error) in
            if result != nil{
                completion(true)
            } else {
                completion(false)
            }
        })
    }

    func onSuccess(invoiceId: String) {
        // The authentication was done successfully, you can send
        // back the invoice ID to your backend to validate the payment
        print("SUCCESS:" + invoiceId)
    }

    func onError(error: ProcessOutException) {
        // An error occurred
        print(error)
    }
}
import com.adyen.threeds2.AuthenticationRequestParameters;
import com.adyen.threeds2.ChallengeStatusReceiver;
import com.adyen.threeds2.CompletionEvent;
import com.adyen.threeds2.ProtocolErrorEvent;
import com.adyen.threeds2.RuntimeErrorEvent;
import com.adyen.threeds2.ThreeDS2Service;
import com.adyen.threeds2.Transaction;
import com.adyen.threeds2.exception.SDKAlreadyInitializedException;
import com.adyen.threeds2.exception.SDKNotInitializedException;
import com.adyen.threeds2.parameters.ChallengeParameters;
import com.adyen.threeds2.parameters.ConfigParameters;
import com.adyen.threeds2.util.AdyenConfigParameters;

// ...

final ThreeDSHandler handler = new ThreeDSHandler() {
    private Transaction mTransaction;

    @Override
    public void doPresentWebView(ProcessOutWebView webView) {
        // Called when a web challenge is required. Present 
        // the webview to the user
    }

    @Override
    public void doFingerprint(DirectoryServerData dsdata, DoFingerprintCallback callback) {
        ConfigParameters config = new AdyenConfigParameters.Builder(
            dsdata.getDirectoryServerID(), 
            dsdata.getDirectoryServerPublicKey()
        ).build();
        try {
            ThreeDS2Service.INSTANCE.initialize(MainActivity.this, config, null, null);
            mTransaction = ThreeDS2Service.INSTANCE.createTransaction(null, null);
            AuthenticationRequestParameters t = mTransaction.getAuthenticationRequestParameters();
            ThreeDSFingerprintResponse gatewayRequest = new ThreeDSFingerprintResponse(
                t.getDeviceData(), 
                t.getSDKAppID(), 
                new Gson().fromJson(t.getSDKEphemeralPublicKey(), SDKEPhemPubKey.class), 
                t.getSDKReferenceNumber(), 
                t.getSDKTransactionID()
            );
            callback.continueCallback(gatewayRequest);
        } catch (SDKAlreadyInitializedException e) {
            e.printStackTrace();
        } catch (SDKNotInitializedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void doChallenge(AuthenticationChallengeData authenticateData, final DoChallengeCallback callback) {
        ChallengeParameters challengeParameters = new ChallengeParameters();
        challengeParameters.set3DSServerTransactionID(authenticateData.getThreeDSServerTransID());
        challengeParameters.setAcsTransactionID(authenticateData.getAcsTransID());
        challengeParameters.setAcsRefNumber(authenticateData.getAcsReferenceNumber());
        challengeParameters.setAcsSignedContent(authenticateData.getAcsSignedContent());

        mTransaction.doChallenge(MainActivity.this, challengeParameters, new ChallengeStatusReceiver() {
            @Override
            public void completed(CompletionEvent completionEvent) {
                callback.success();
            }

            @Override
            public void cancelled() {
                // Cancelled by the user or the App.
                callback.error();
            }

            @Override
            public void timedout() {
                // The user didn't submit the challenge within the given time, 5 minutes in this case.
                callback.error();
            }

            @Override
            public void protocolError(ProtocolErrorEvent protocolErrorEvent) {
                // An error occurred.
                callback.error();
            }

            @Override
            public void runtimeError(RuntimeErrorEvent runtimeErrorEvent) {
                // An error occurred.
                callback.error();
            }
        }, 5);
    }

    @Override
    public void onSuccess(String invoiceId) {
        // The authentication was done successfully, you can send
        // back the invoice ID to your backend to validate the payment
        Log.d("PROCESSOUT", "SUCCESS:" + invoiceId);
    }

    @Override
    public void onError(Exception error) {
        // An error occurred
        Log.e("PROCESSOUT", error.toString());
    }
};