Skip to content

Epos SDK Design Principles

Wirecard ePOS SDK heavily relies on RxJava2 reactive principles. To learn more about the observer pattern and RxJava, consult the documentation on RxJava's github or the official ReactiveX webpage. Every call to Wirecard ePOS SDK returns an object which extends the observer pattern. It allows you to:

  • choose between synchronous or asynchronous results
  • manage execution and result threads
  • manage a chain of methods by operators
  • and much more

Note

Every example in this documentation is using asynchronous approach with lambda expressions.

Completable, Single, Observable

Most methods return Completable or Single object. They fulfil the observer pattern and can be used as regular RxJava observables. The result of a Completable object is a simple 'complete' answer, which indicates if everything was performed correctly. Single objects return as a response exactly one result, which is defined with generics. These two are used for most calls to SDK. Observable objects can return object defined by generics multiple times.

CompletableParallel and SingleParallel

These two objects are special because they contain two separate rx streams:

  • Main stream contains final result of request and
  • Second stream contains information update events, some progress information or even requests from the user. Second stream can be accessed using the subscribeParallel( ) method.

Here is a recommended usage:

import com.jakewharton.rxrelay2.BehaviorRelay;
import com.jakewharton.rxrelay2.Relay;
import io.reactivex.android.schedulers.AndroidSchedulers;


Relay<Event> eventObservable = BehaviorRelay.create();

epos.methodReturnsSingleParallel()
    .subscribeParallel(eventObservable)
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(onSuccess -> {
            //do something on success
        }, onError -> {
            //show error
        }
    );

eventObservable
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(event -> {
        //do something on event
    });                                      

Synchronous vs Asynchronous

You can choose which principle suits your algorithms best for every method call.

Synchronous Methods

A synchronous response can be achieved by calling one of these methods:

//completable
epos.methodReturnsCompletable().blockingAwait()    //returns void or rethrows any exception emitted
epos.methodReturnsCompletable().blockingGet()      //returns null or the emitted exception if any

//single
epos.methodReturnsSingle().blockingGet()           //returns success object or rethrows any exception emitted                                  

Asynchronous Methods

An asynchronous response can be achieved by calling the subscribe method with various parameters. Every subscribe method returns Disposable, and this can be used to cancel the subscription. The result comes as an asynchronous callback. You can manage threads where the response came.

//completable
epos.methodReturnsCompletable().subscribe()                                                        //returns disposable, ignores all responses
epos.methodReturnsCompletable().subscribe(CompletableObserver s)                                   //returns disposable, all responses came in given CompletableObserver
epos.methodReturnsCompletable().subscribe(Action onComplete, Consumer<? super Throwable> onError)  //returns disposable, successful responses came in onComplete Action and error in onError
epos.methodReturnsCompletable().subscribe(Action onComplete)                                       //returns disposable, successful response came in given Action, error is ignored

//single
epos.methodReturnsSingle().subscribe()                                                                     //returns disposable, ignores all responses
epos.methodReturnsSingle().subscribe(BiConsumer<? super T, ? super Throwable> onCallback)                  //returns disposable, all responses came in given composite BiConsumer callback
epos.methodReturnsSingle().subscribe(Consumer<? super T> onSuccess)                                        //returns disposable, successful response came in given Consumer callback, error is ignored
epos.methodReturnsSingle().subscribe(Consumer<? super T> onSuccess, Consumer<? super Throwable> onError)   //returns disposable, successful or error response came in given onSuccess or onError Consumer
epos.methodReturnsSingle().subscribe(SingleObserver<? super T> subscriber)                                 //returns disposable, all responses came in given SingleObserver                                     

Threads

There is no need for you to take care of execution of SDK methods. All executions are properly executed on threads, provided by the SDK. However, if you choose the asynchronous approach, you should take care of the result thread using rx schedulers. By default, responses will come in the SDK thread, which cannot manipulate Android Views. For more information, consult the rx documentation related to schedulers and RxAndroid.

You can change the result thread by calling observeOn method, as shown:

epos.anyObservableMethod()
  .observeOn(AndroidSchedulers.mainThread())
  .subscribe(...);                                     

Operators

Operators are a very powerful RxJava mechanism. See this page for more information about them. The code below illustrates a simple example of a chain of sdk calls that use the andThen and flatMap operators.

epos.methodReturnsCompletable()
    .andThen(epos.methodReturnsAnotherCompletable())
    .andThen(epos.methodReturnsSingle())
    .flatMap(previousResult -> epos.methodReturnsAnotherSingle())
    .subscribe(...);

Is there a non Rx way?

If you're not familiar with RxJava and Rx principles, you can use SDK with "listeners" as you're used to. Just call subscribe method to every SDK method and put listeners as parameters.

//completable
eposSDK.methodReturnsCompletable().subscribe(new Action() {
            @Override
            public void run() throws Exception {
                ...
            }
        }, new Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) throws Exception {
                ...
            }
        });
eposSDK.methodReturnsCompletable().subscribe(new CompletableObserver() {
            @Override
            public void onSubscribe(Disposable d) {
                ...
            }

            @Override
            public void onComplete() {
                ...
            }

            @Override
            public void onError(Throwable e) {
                ...
            }
        });

//single
eposSDK.methodReturnsSingle().subscribe(new BiConsumer<Response, Throwable>() {
            @Override
            public void accept(Response response, Throwable throwable) throws Exception {
                ...
            }
        });
eposSDK.methodReturnsSingle().subscribe(new Consumer<Response>() {
            @Override
            public void accept(Response response) throws Exception {
                ...
            }
        }, new Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) throws Exception {
                ...
            }
        });
eposSDK.methodReturnsSingle().subscribe(new SingleObserver<Response>() {
            @Override
            public void onSubscribe(Disposable disposable) {
                ...
            }

            @Override
            public void onSuccess(Response response) {
                ...
            }
                ...
            @Override
            public void onError(Throwable e) {

            }
        });

Parameter "With"

"With" method parameter gives you advanced control over processes started by SDK methods.

You can run backend calls on desired thread/scheduler. Requests, by default, are running on thread that is related to specific manager (e.g SaleManager methods runs on "SalesThread" by default). This can be changed by using "scheduler()" method of an "With" interface.

When you start backend request the response object can contain huge amount of information stored in the response object fields and some of them may not be useful for your application. To avoid receiving all response object fields the "includeResponseFields()" and the "excludeResponseFields()" methods can help you to reduce the amount of response object fields. If you use "includeResponseFields()" method the response object will contain only those fields which you have added to this method as parameters (e.g. you will receive only: "transaction", "id", "type", "state" response object fields instead of receiving others that you don't even need for your application).
Similar usage is for "excludeResponseFields()" method where you will receive the response object that will not contain those response object fields which you have included in this method.

There is possibility to add optional parameters to backend request. For example, if you are using backend with multiple users but you want to build request, where you will receive only one specific user with name "user26" you can use "param()", "params()" methods. Backend request for users has optional parameter "username". Using couple param("username", "user26")in request for users you will receive desired user object from backend.

Pagination of request object can be achieved by using "page()" and "size()" methods. Imagine you have backend with 1000 users. You want to get only first 20 users. You need to use "page(1)" and "size(20)" and you will receive first 20 users. If you want to get 20 more users you will use "page(2)" and "size(20)".

Response results can be sorted also. There are "ASC" and "DESC" enumerators. "ASC" means ascending and "DESC" means descending order. There is possibility to combine multiple order params. To sort your backend response object users by response object field "name" from A to Z you will need to use "sort("username", WithPagination.Order.ASC)". Imagine, at the same time you want to sort the same backend response object users by response object field "age" but from highest to lowest. You will use "sort("age", WithPagination.Order.DESC)". You have already applied two sorts in one backend request. The result object will contain users sorted by "name" alphabetically by fist and by "age" from highest to lowest as a second.

There are three types of "With" parameters where you can use apply above mentioned params:

  • WithBase
  • WithFields
  • WithPagination
epos.anyObservableMethod(With with)
  .observeOn(...)
  .subscribe(...);                                     

WithBase

Methods:

  • scheduler(Scheduler scheduler)
    You can set desired Scheduler upon which the request will run.
WithBase withBase = With.base().scheduler(Schedulers.io());

WithFields

Methods:

  • scheduler(Scheduler scheduler)
    You can set desired Scheduler upon which the request will run.

  • includeResponseFields(String[] includeResponseFields)
    Fields that will be retrieved from original response object so the response object will contain only "includeResponseFields".

  • excludeResponseFields(String[] excludeResponseFields)
    Fields that will be omitted from original response object (original response object minus excludeResponseFields).

  • param(String name, String value)
    Optional request parameter that will be included in backend request.

  • params(Pair values)
    Optional request parameters that will be included in backend request.

List of fields are per request and can be checked on https://switch-test.wirecard.com/mswitch-server/swagger-ui.html

In the next snippet, there are used params "id, type, status, currency" for include response fields.

SDK param Swagger equivalent Description
includeResponseFields() field Fields to retrieve. Can be specified multiple times. Separate nested sub-fields with dot. If no field is provided then result with all fields will be retrieved.
excludeResponseFields() excludeField Fields to omit from the result. Can be specified multiple times. Separate nested sub-fields with dot.

WithFields withFields = With.fields()
                            .scheduler(Schedulers.newThread())
                            .includeResponseFields(new String[]{"id", "type", "status", "currency"})
                            .param("groupBy", "STATUS")
                            .param("countryCode", "BTN")
                            .params(new Pair<String, String>("statuses", "COMPLETED"),
                                    new Pair<String, String>("statuses", "CANCELED"));
It can be used for example for getSales() request and swagger equivalent is when to section "field" under sales/GET/v1/sales put the same params "id, type, status, currency".

WithPagination

Methods:

  • scheduler(Scheduler scheduler)
    You can set desired Scheduler upon which the request will run.

  • includeResponseFields(String[] includeResponseFields)
    Fields that will be retrieved from original response object so the response object will contain only "includeResponseFields".

  • excludeResponseFields(String[] excludeResponseFields)
    Fields that will be omitted from original response object (original response object minus excludeResponseFields).

  • param(String name, String value)
    Optional request parameter that will be included in backend request.

  • params(Pair values)
    Optional request parameters that will be included in backend request.

  • page(Int page)
    Pagination of results can be achieved by adding "page" and "size" into backend request parameters.

  • size(Int size) maximum 1000
    Number of response results.

  • sort(String property, WithPagination.Order order)
    Response objects will be sorted according to "property" that will be added into backend request (there are two options: ASC, DESC).

WithPagination withPagination = With.pagination()
                                    .scheduler(Schedulers.newThread())
                                    .includeResponseFields(new String[]{"id", "name", "openTime"})
                                    .param("groupBy", "STATUS") // optional request parameter
                                    .page(0)
                                    .size(20)
                                    .sort("openTime", WithPagination.Order.ASC)