- Getting Started
- Tutorials
- Reference
- API Reference
- Basic Payment
- Authentication
- Card Account
- Virtual Account
- Bank Account
- Customer
- Billing Address
- Shipping Address
- Merchant
- Corporate
- Cart
- Airline
- Tokenization
- Recurring
- Recurring Migration
- 3D Secure
- Custom Parameters
- Async Payments
- Webhook notifications
- Risk
- Point of Sale
- Response Parameters
- Card On File
- Chargeback
- Result Codes
- Brands Reference
- API Reference
- FAQ

SDK Integration
This article will guide you through the integration of a native payment experience in your mobile shop.
Accepting payment in your app involves 4 steps:
- Preparing checkout (configure with amount, currency and other information),
- Collecting shopper payment details,
- Creating and submitting transaction,
- Requesting payment result.
iOS and Android SDK provide tools to help you with steps 2 and 3.
For steps 1 and 4 you will need to communicate to your own backend API. These steps are not included in SDK API due to security reasons.
Demo app
You are welcome to get started with our demo application. You can start making test transactions without any efforts including setting up a server, we provide a test integration server for you. Please note, that it is configured for demo purposes only.
The demo app is provided along with SDK since version 2.5.0.
NOTE: Before running the application, make sure SDK files are copied to the demo app folder. Find more details in the readme.txt file.
Install the SDK
iOS
- Drag and drop
OPPWAMobile.framework
&OPPWAMobile-Resources.bundle
to the "Frameworks" folder of your project.
Make sure "Copy items if needed" is checked.
- Check "Build Phases" section of your Xcode project settings. Make sure:
.framework
file is in "Link Binaries with Libraries",.bundle
file is in "Copy Bundle Resources".- Confirm that these two Build Settings are both enabled:
Enable Modules (C and Objective-C)
Link Frameworks Automatically
You can now import the framework with:
#import <OPPWAMobile/OPPWAMobile.h>
#import <OPPWAMobile/OPPWAMobile.h>
NOTE: If you integrate SDK to the Swift app, please see the Apple guide Importing Objective-C into Swift to check how to import Objective-C headers correctly.
In your checkout controller or wherever else you handle payments, create the OPPPaymentProvider
variable and initialize it with test mode, e.g. in the viewDidLoad
method:
@property (nonatomic) OPPPaymentProvider *provider;
- (void)viewDidLoad {
[super viewDidLoad];
self.provider = [OPPPaymentProvider paymentProviderWithMode:OPPProviderModeTest];
}
override func viewDidLoad() {
super.viewDidLoad()
let provider = OPPPaymentProvider(mode: .test)
}
Android
- Download the Mobile SDK for Android.
- Open your
AndroidManifest.xml
and declare the connect service within the application tag: - In addition, declare the "INTERNET" permission before the application tag:
-
Import
oppwa.mobile.aar
into your project (File > New > New Module > Import .JAR/.AAR Package).
Then, reference it from your application (File > Project Structure > Dependencies > Add Module Dependency).
<service
android:name="com.oppwa.mobile.connect.service.ConnectService"
android:exported="false"/>
If you want to use prebuilt checkout also declare the CheckoutActivity:
<activity
android:name="com.oppwa.mobile.connect.checkout.dialog.CheckoutActivity"
android:theme="@style/Theme.Checkout.Light"
android:windowSoftInputMode="adjustPan"/>
<uses-permission android:name="android.permission.INTERNET"/>
Set Up Your Server
To start working with our SDK, you should expose two APIs on your backend for your app to communicate with:
- Endpoint 1: Creating a checkout ID,
- Endpoint 2: Getting result of payment.
See detailed instruction in our guide "Set Up Your Server".
Request Checkout ID
Your app should request a checkout ID from your server. This example uses our sample integration server; please adapt it to use your own backend API.
NSURLRequest *merchantServerRequest = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://YOUR_URL/?amount=100¤cy=EUR&paymentType=DB"]];
[[[NSURLSession sharedSession] dataTaskWithRequest:merchantServerRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// handle error
NSDictionary *JSON = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
self.checkoutID = JSON[@"checkoutId"];
}] resume];
let merchantServerRequest = NSURLRequest(url: URL(string: "https://YOUR_URL/?amount=100¤cy=EUR&paymentType=DB")!)
URLSession.shared.dataTask(with: merchantServerRequest as URLRequest) { (data, response, error) in
// TODO: Handle errors
if let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
let checkoutID = json?["checkoutId"] as? String
}
}.resume()
public String requestCheckoutId() {
URL url;
String urlString;
HttpURLConnection connection = null;
String checkoutId = null;
urlString = YOUR_URL + "?amount=48.99¤cy=EUR&paymentType=DB";
try {
url = new URL(urlString);
connection = (HttpURLConnection) url.openConnection();
JsonReader reader = new JsonReader(
new InputStreamReader(connection.getInputStream(), "UTF-8"));
reader.beginObject();
while (reader.hasNext()) {
if (reader.nextName().equals("checkoutId")) {
checkoutId = reader.nextString();
break;
}
}
reader.endObject();
reader.close();
} catch (Exception e) {
/* error occurred */
} finally {
if (connection != null) {
connection.disconnect();
}
}
return checkoutId;
}
fun requestCheckoutId(): String? {
val url: URL
var connection: HttpURLConnection? = null
var checkoutId: String? = null
val urlString = YOUR_URL.toString() + "?amount=48.99¤cy=EUR&paymentType=DB"
try {
url = URL(urlString)
connection = url.openConnection() as HttpURLConnection
val reader = JsonReader(InputStreamReader(connection.inputStream, "UTF-8"))
reader.beginObject()
while (reader.hasNext()) {
if (reader.nextName() == "checkoutId") {
checkoutId = reader.nextString()
break
}
}
reader.endObject()
reader.close()
} catch (e: Exception) {
/* error occurred */
} finally {
connection?.disconnect()
}
return checkoutId
}
Collect and validate shopper payment details
First you need to create an OPPPaymentParams
object and initialize it with collected shopper payment details, it's required for processing a transaction.
Use fabric initializers to create an appropriate subclass of OPPPaymentParams
. See code sample for credit card payment params:
NSError *error = nil;
OPPCardPaymentParams *params = [OPPCardPaymentParams cardPaymentParamsWithCheckoutID:self.checkoutID
paymentBrand:@"VISA"
holder:holderName
number:cardNumber
expiryMonth:month
expiryYear:year
CVV:CVV
error:&error];
if (error) {
// See error.code (OPPErrorCode) and error.localizedDescription to identify the reason of failure
}
// Set shopper result URL
params.shopperResultURL = @"com.companyname.appname.payments://result";
do {
let params = try OPPCardPaymentParams(checkoutID: checkoutID, paymentBrand: "VISA", holder: holderName, number: cardNumber, expiryMonth: month, expiryYear: year, cvv: CVV)
// Set shopper result URL
params.shopperResultURL = "com.companyname.appname.payments://result"
} catch let error as NSError {
// See error.code (OPPErrorCode) and error.localizedDescription to identify the reason of failure
}
NOTE: To learn more about shopper result URL refer to Asynchronous payments guide.
You can also validate each parameter separately before creating an OPPPaymentParams
object.
iOS SDK provides convenience validation methods for each payment parameter. See code sample for card number validation:
if (![OPPCardPaymentParams isNumberValid:@"4200 0000 0000 0000" luhnCheck:YES]) {
// display error that card number is invalid
}
if !OPPCardPaymentParams.isNumberValid("4200 0000 0000 0000", luhnCheck: true) {
// display error that card number is invalid
}
First you need to create an PaymentParams
object and initialize it with collected shopper payment details, it's required for processing a transaction.
Use fabric initializers to create an appropriate subclass of PaymentParams
. See code sample for credit card payment params:
PaymentParams paymentParams = new CardPaymentParams(
checkoutId,
brand,
number,
holder,
expiryMonth,
expiryYear,
cvv
);
// Set shopper result URL
paymentParams.setShopperResultUrl("companyname://result");
val paymentParams: PaymentParams = CardPaymentParams(
checkoutId,
brand,
number,
holder,
expiryMonth,
expiryYear,
cvv
)
// Set shopper result URL
paymentParams.shopperResultUrl = "companyname://result"
NOTE: To learn more about shopper result URL refer to Asynchronous payments guide.
You can also validate each parameter separately before creating an PaymentParams
object. Android mSDK provides convenience validation methods for each payment parameter. See code sample for card number validation:
if (!CardPaymentParams.isNumberValid("4200 0000 0000 0000")) {
/* display error that card number is invalid */
}
if (!CardPaymentParams.isNumberValid("4200 0000 0000 0000")) {
/* display error that card number is invalid */
}
[Optional] Brand detection for card payments
The system is able to automatically detect the brand by analyzing credit card number. The feature can be activated in BIP at 'Administration' -> 'Processing' -> 'Processing Settings' -> 'Activate automatic brand detection for missing brands of credit card accounts'.
Since the mSDK version 2.25.0 you can create OPPCardPaymentParams
CardPaymentParams
without specifying a payment brand.
Use special constructor for it (sample below) or just pass empty string instead of payment brand in the standard constructor.
[OPPCardPaymentParams cardPaymentParamsWithCheckoutID:self.checkoutID
holder:holderName
number:cardNumber
expiryMonth:month
expiryYear:year
CVV:CVV
error:&error];
OPPCardPaymentParams(checkoutID: checkoutID, holder: holderName, number: cardNumber, expiryMonth: month, expiryYear: year, cvv: CVV)
new CardPaymentParams(checkoutId, number, holder, expiryMonth, expiryYear, cvv);
CardPaymentParams(checkoutId, number, holder, expiryMonth, expiryYear, cvv)
Submit a transaction
Create a transaction with the collected valid payment parameters. Then submit it using OPPPaymentProvider
method and implement the callback.
Check type of the transaction returned in the callback:
- if it's synchronous payment, you can immediately request payment result from your server,
- otherwise transaction will contain redirect url, shopper should be redirected to this URL to pass additional checks on payment provider side (e.g. pass 3D Secure check).
NOTE: To support asynchronous payments you need to make additional changes in your app. See Asynchronous payments tutorial for more details.
OPPTransaction *transaction = [OPPTransaction transactionWithPaymentParams:params];
[self.provider submitTransaction:transaction completionHandler:^(OPPTransaction * _Nonnull transaction, NSError * _Nullable error) {
if (transaction.type == OPPTransactionTypeAsynchronous) {
// Open transaction.redirectURL in Safari browser to complete the transaction
} else if (transaction.type == OPPTransactionTypeSynchronous) {
// Send request to your server to obtain transaction status
} else {
// Handle the error
}
}];
let transaction = OPPTransaction(paymentParams: params)
provider.submitTransaction(transaction) { (transaction, error) in
guard let transaction = transaction else {
// Handle invalid transaction, check error
return
}
if transaction.type == .asynchronous {
// Open transaction.redirectURL in Safari browser to complete the transaction
} else if transaction.type == .synchronous {
// Send request to your server to obtain transaction status
} else {
// Handle the error
}
}
Create a transaction with the collected valid payment parameters. Then submit it using PaymentProvider
method and implement the callback.
Check type of the transaction returned in the callback:
- if it's synchronous payment, you can immediately request payment result from your server,
- otherwise transaction will contain redirect url, shopper should be redirected to this URL to pass additional checks on payment provider side (e.g. pass 3D Secure check).
NOTE: To support asynchronous payments you need to make additional changes in your app. See Asynchronous payments tutorial for more details.
NOTE: To avoid a 'javax.net.ssl.SSLProtocolException: SSL handshake aborted' on the devices running Android 4.4 (API level 19) and below you need to call installIfNeeded() method of the ProviderInstaller. You can place this call in your first Activity. See more info here.
private IProviderBinder providerBinder;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
providerBinder = (IProviderBinder) service;
/* we have a connection to the service */
try {
providerBinder.initializeProvider(Connect.ProviderMode.TEST);
} catch (PaymentException ee) {
/* error occurred */
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
providerBinder = null;
}
};
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, ConnectService.class);
startService(intent);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
unbindService(serviceConnection);
stopService(new Intent(this, ConnectService.class));
}
private var providerBinder: IProviderBinder? = null
private val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
providerBinder = service as IProviderBinder
/* we have a connection to the service */
try {
providerBinder!!.initializeProvider(Connect.ProviderMode.TEST)
} catch (ee: PaymentException) {
/* error occurred */
}
}
override fun onServiceDisconnected(name: ComponentName) {
bproviderBinder = null
}
}
override fun onStart() {
super.onStart()
val intent = Intent(this, ConnectService::class.java)
startService(intent)
bindService(intent, serviceConnection, BIND_AUTO_CREATE)
}
override fun onStop() {
super.onStop()
unbindService(serviceConnection)
stopService(Intent(this, ConnectService::class.java))
}
Transaction transaction = null;
try {
transaction = new Transaction(paymentParams);
providerBinder.submitTransaction(transaction);
} catch (PaymentException ee) {
/* error occurred */
}
try {
val transaction = Transaction(paymentParams)
providerBinder.submitTransaction(transaction)
} catch (ee: PaymentException) {
/* error occurred */
}
Now, let the class implement the ITransactionListener
interface. After initializing the provider add the transaction listener
providerBinder.addTransactionListener(YourActivity.this);
providerBinder.addTransactionListener(this@YourActivity)
and implement the following ITransactionListener
methods:
@Override
public void transactionFailed(Transaction transaction, PaymentError paymentError) {
/* add your implementation here */
}
@Override
public void transactionCompleted(Transaction transaction) {
/* add your implementation here */
}
override fun transactionFailed(transaction: Transaction, paymentError: PaymentError) {
/* add your implementation here */
}
override fun transactionCompleted(transaction: Transaction) {
/* add your implementation here */
}
NOTE: The getErrorInfo() method of the PaymentError can be used to get more details about the error.
Request Payment Status
Finally your app should request the payment status from your server (again, adapt this example to your own setup).
NSString *URL = [NSString stringWithFormat:@"https://YOUR_URL/paymentStatus?resourcePath=%@", [checkoutInfo.resourcePath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLRequest *merchantServerRequest = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:URL]];
[[[NSURLSession sharedSession] dataTaskWithRequest:merchantServerRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// handle error
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
BOOL status = [result[@"paymentResult"] boolValue];
}] resume];
let url= String(format: "https://YOUR_URL/paymentStatus?resourcePath=%@", checkoutInfo.resourcePath!.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)
let merchantServerRequest = NSURLRequest(url: URL(string: url)!)
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
let transactionStatus = json?["paymentResult"] as? Bool
}
}.resume()
To get resourcePath
you should make additional request to the server.
[self.provider requestCheckoutInfoWithCheckoutID:checkoutId completionHandler:^(OPPCheckoutInfo * _Nullable checkoutInfo, NSError * _Nullable error) {
if (error) {
// Handle error
} else {
// Use checkoutInfo.resourcePath for getting transaction status
}
}];
self.provider.requestCheckoutInfo(withCheckoutID: checkoutId, completionHandler: { (checkoutInfo, error) in
guard let resourcePath = checkoutInfo?.resourcePath else {
// Handle error
return
}
// Use resourcePath for getting transaction status
})
public String requestPaymentStatus() {
URL url;
String urlString;
HttpURLConnection connection = null;
String paymentStatus = null;
urlString = YOUR_URL + "/paymentStatus?resourcePath=" + URLEncoder.encode(RESOURCE_PATH, "UTF-8");
try {
url = new URL(urlString);
connection = (HttpURLConnection) url.openConnection();
JsonReader jsonReader = new JsonReader(
new InputStreamReader(connection.getInputStream(), "UTF-8"));
jsonReader.beginObject();
while (jsonReader.hasNext()) {
if (jsonReader.nextName().equals("paymentResult")) {
paymentStatus = jsonReader.nextString();
break;
}
}
jsonReader.endObject();
jsonReader.close();
} catch (Exception e) {
/* error occurred */
} finally {
if (connection != null) {
connection.disconnect();
}
}
return paymentStatus;
}
fun requestPaymentStatus(): String? {
val url: URL
var connection: HttpURLConnection? = null
var paymentStatus: String? = null
val urlString = YOUR_URL.toString() + "/paymentStatus?resourcePath=" + URLEncoder.encode(RESOURCE_PATH, "UTF-8")
try {
url = URL(urlString)
connection = url.openConnection() as HttpURLConnection
val jsonReader = JsonReader(InputStreamReader(connection.inputStream, "UTF-8"))
jsonReader.beginObject()
while (jsonReader.hasNext()) {
if (jsonReader.nextName() == "paymentResult") {
paymentStatus = jsonReader.nextString()
break
}
}
jsonReader.endObject()
jsonReader.close()
} catch (e: Exception) {
/* error occurred */
} finally {
connection?.disconnect()
}
return paymentStatus
}
To get the resourcePath
you should make the additional request to the server.
try {
providerBinder.requestCheckoutInfo(CHECKOUT_ID);
} catch (PaymentException e) {
/* error occurred */
}
@Override
public void paymentConfigRequestSucceeded(CheckoutInfo checkoutInfo) {
/* get the resource path in this ITransactionListener callback */
String resourcePath = checkoutInfo.getResourcePath();
}
try {
providerBinder.requestCheckoutInfo(CHECKOUT_ID)
} catch (e: PaymentException) {
/* error occurred */
}
override fun paymentConfigRequestSucceeded(checkoutInfo: CheckoutInfo) {
/* get the resource path in this ITransactionListener callback */
val resourcePath = checkoutInfo.resourcePath
}
Testing
The sandbox environment only accepts test payment parameters.
You can find test values for credit cards and bank accounts in our Testing Guide.
Go to Production
1. Talk to your account manager to get the live credentials.
2. Adjust the server type to LIVE in your initialization of OPPPaymentProvider
.
self.provider = [OPPPaymentProvider paymentProviderWithMode:OPPProviderModeLive];
let provider = OPPPaymentProvider(mode: OPPProviderMode.live)
3. Change your backend to use the correct API endpoints and credentials.
2. Adjust the server type to LIVE in your initialization of PaymentProvider
.
providerBinder.initializeProvider(Connect.ProviderMode.LIVE);
providerBinder.initializeProvider(Connect.ProviderMode.LIVE)