- Get Started
- Guides
- Integrations
- References
- API Reference
- Basic Payment
- Forex
- Authentication
- Card Account
- Apple Pay
- Virtual Account
- Bank Account
- Token Account
- Customer
- Billing Address
- Merchant Billing Address
- Shipping Address
- Merchant Shipping Address
- Merchant
- Corporate
- Recipient
- Marketplace & Cart
- Airline
- Lodging
- Passenger
- Tokenization
- Recurring Migration
- 3D Secure
- Custom Parameters
- Async Payments
- Webhook notifications
- Job
- Risk
- Response Parameters
- Card On File
- Chargeback
- Result Codes
- Payment Methods
- Transaction Flows
- Regression Testing
- Data Retention Policy
- API Reference
- Support
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.xcframework
to the "Frameworks" folder of your project.
Make sure "Copy items if needed" is checked.
- Check "Frameworks, Libraries, and Embedded Content" section under the general settings tab of your application's target. Ensure the Embed dropdown has Embed and Sign selected for the framework.
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
- Import the Mobile SDK for Android.
Add the module and required dependencies to your
build.gradle
:- In addition, declare the "INTERNET" permission before the application tag:
Download oppwa.mobile.aar
and import it as the module into your project:
File > New > New Module > Import .JAR/.AAR Package
// this name must match the library name defined with an include: in your settings.gradle file
implementation project(":oppwa.mobile")
implementation "androidx.appcompat:appcompat:x.x.x"
implementation "com.google.android.material:material:x.x.x"
implementation "com.google.android.gms:play-services-base:x.x.x"
<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 FS Technologies Platform 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
// Error is no more an decisive factor for transaction termination
if (transaction.resourcePath) {
// Get the payment status using the resourcePath.
}
}
}];
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
// Error is no more an decisive factor for transaction termination
if transaction.resourcePath {
// Get the payment status using the resourcePath.
}
}
}
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.
NOTE: ServiceConnection service deprecated, please use OPPPaymentProvider instead.
Transaction transaction = null;
try {
OppPaymentProvider paymentProvider = new OppPaymentProvider(context, Connnect.ProviderMode.TEST);
transaction = new Transaction(paymentParams);
paymentProvider.submitTransaction(transaction, transactionListener);
} catch (PaymentException ee) {
/* error occurred */
}
try {
val paymentProvider = OppPaymentProvider(context, Connnect.ProviderMode.TEST)
val transaction = Transaction(paymentParams)
paymentProvider.submitTransaction(transaction, transactionListener)
} catch (ee: PaymentException) {
/* error occurred */
}
Now, let the class implement the ITransactionListener
interface.
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.
Get the 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 {
paymentProvider.requestCheckoutInfo(CHECKOUT_ID, transactionListener);
} catch (PaymentException e) {
/* error occurred */
}
@Override
public void paymentConfigRequestSucceeded(CheckoutInfo checkoutInfo) {
/* get the resource path in this ITransactionListener callback */
String resourcePath = checkoutInfo.getResourcePath();
}
try {
paymentProvider.requestCheckoutInfo(CHECKOUT_ID, transactionListener)
} 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
.
paymentProvider.setProviderMode(Connect.ProviderMode.LIVE);
paymentProvider.setProviderMode(Connect.ProviderMode.LIVE)
Or simply init OPPPaymentPaymentProvider with ProviderMode.LIVE.
OppPaymentProvider paymentProvider = new OppPaymentProvider(context, Connnect.ProviderMode.LIVE);
val paymentProvider = OppPaymentProvider(context, Connnect.ProviderMode.LIVE)