First integration
This guide helps you make the first payment using ready-to-use payment forms.
Demo app
Get started with the demo application to make test transactions without setting up a server. A test integration server supports these transactions for convenience, but it runs for demo purposes.
The SDK includes the demo app starting from version 2.5.0.
Before running the application, copy the SDK files to the demo app folder. Find more details in the
readme.txtfile.
The full document in markdown, formatted according to your preferences are:
iOS
Requirements
- Xcode 16 and iOS 18 SDK or higher
- iOS 13.0+ deployment target
Install the mSDK
- Drag
OPPWAMobile.xcframeworkandipworks3ds_sdk.xcframeworkinto the Frameworks folder of the project. Check Copy items if needed. - Under Frameworks, Libraries, and Embedded Content in the General settings tab, set the Embed drop-down to Embed and Sign.
Import the framework by adding the following import statement to use the SDK:
#import <OPPWAMobile/OPPWAMobile.h>import OPPWAMobileThe SDK includes two versions of ipworks3ds_sdk.xcframework:
- Development version: For debugging and testing.
- Production version: Includes stricter security, preventing debugging or simulator use. The production version includes
_deployin the filename.
If integrating the SDK into a Swift app, follow Apple's Importing Objective-C into Swift guide.
Set up the server
To use the SDK, expose two APIs on the backend for the iOS app to communicate with:
- Endpoint 1: Create a checkout ID
- Endpoint 2: Retrieve the payment result
Refer to the set up your server guide for detailed instructions.
Request a checkout ID
The app must request a checkout ID from the backend. The following example demonstrates how to request a checkout ID using a sample integration server. Adapt it for your 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) {
// Ensure that you handle errors
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
// Ensure that you handle errors
if let data = data, let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
let checkoutID = json?["checkoutId"] as? String
}
}.resume()Present the checkout UI
1. Initialise the payment provider
Set up the payment provider in test mode:
self.provider = [OPPPaymentProvider paymentProviderWithMode:OPPProviderModeTest];let provider = OPPPaymentProvider(mode: OPPProviderMode.test)2. Configure the checkout settings
Define the settings for the checkout screen using OPPCheckoutSettings. Initialise OPPCheckoutSettings to control the information displayed to the shopper. For the first integration, enable synchronous payment methods. Other payment brands require further configuration.
OPPCheckoutSettings *checkoutSettings = [[OPPCheckoutSettings alloc] init];
// Set available payment brands for the shop
checkoutSettings.paymentBrands = @[@"VISA", @"DIRECTDEBIT_SEPA"];
// Set shopper result URL
checkoutSettings.shopperResultURL = @"com.companyname.appname.payments://result";let checkoutSettings = OPPCheckoutSettings()
// Set available payment brands for your shop
checkoutSettings.paymentBrands = ["VISA", "DIRECTDEBIT_SEPA"]
// Set shopper result URL
checkoutSettings.shopperResultURL = "com.companyname.appname.payments://result"To support all payment methods, set
shopperResultURL. See the Asynchronous payments guide for more details.
3. Present the checkout page
Start OPPCheckoutProvider with the received checkout ID and settings.
Create the
OPPCheckoutProviderobject at the class level to ensure its not released during the payment process.
OPPCheckoutProvider *checkoutProvider = [OPPCheckoutProvider checkoutProviderWithPaymentProvider:provider
checkoutID:checkoutID
settings:checkoutSettings];let checkoutProvider = OPPCheckoutProvider(paymentProvider: provider, checkoutID: checkoutID!, settings: checkoutSettings)Open the payment page and handle callbacks:
// Since version 2.13.0
[checkoutProvider presentCheckoutForSubmittingTransactionCompletionHandler:^(OPPTransaction * _Nullable transaction, NSError * _Nullable error) {
if (transaction.type == OPPTransactionTypeSynchronous) {
// Send request to your server to obtain the status of the synchronous transaction
// You can use transaction.resourcePath or just checkout id to do it
// Error is no more an decisive factor for transaction termination
if (transaction.resourcePath) {
// get the payment status using the resourcePath
}
} else {
// The SDK opens transaction.redirectUrl in a browser
// See 'Asynchronous Payments' guide for more details
}
} cancelHandler:^{
// Executed if the shopper closes the payment page prematurely
}];For SDK versions 2.12.0 and earlier, use an alternative method to open the checkout page. The paymentBrandSelectedHandler callback lets you create and pass a new checkout ID to the SDK. Newer versions move this callback to OPPCheckoutProviderDelegate. Use this feature to send more parameters at the first step for specific payment brands. Otherwise, set the callback to nil.
[checkoutProvider presentCheckoutForSubmittingTransactionCompletionHandler:^(OPPTransaction * _Nullable transaction, NSError * _Nullable error) {
// Handle transaction submitting result as shown in previous sample
} paymentBrandSelectedHandler:nil cancelHandler:^{
// Executed if the shopper closes the payment page prematurely
}];checkoutProvider?.presentCheckout(forSubmittingTransactionCompletionHandler: { (transaction, error) in
// Handle transaction submitting result as shown in previous sample
}, paymentBrandSelectedHandler:nil, cancelHandler: {
// Executed if the shopper closes the payment page prematurely
})Close the payment page when the app moves to the background to protect the shopper's credit card data. Use
dismissCheckoutAnimated:completion:ofOPPCheckoutProvider.
4. [Optional] Enhance security by disabling third-party keyboards
A malicious third-party keyboard may log keystrokes and transfer sensitive data. Disabling third-party keyboards can enhance security but may impact user experience.
- (BOOL)application:(UIApplication *)application shouldAllowExtensionPointIdentifier:(UIApplicationExtensionPointIdentifier)extensionPointIdentifier {
if (extensionPointIdentifier == UIApplicationKeyboardExtensionPointIdentifier) {
return NO;
}
return YES;
}func application(_ application: UIApplication, shouldAllowExtensionPointIdentifier extensionPointIdentifier: UIApplicationExtensionPointIdentifier) -> Bool {
if (extensionPointIdentifier == UIApplicationExtensionPointIdentifier.keyboard) {
return false
}
return true
}Get the payment status
The app must request the payment status from the server for any result except cancelled.
NSString *URL = [NSString stringWithFormat:@"https://YOUR_URL/paymentStatus?resourcePath=%@",
[transaction.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=%@", transaction.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()The transaction status may not be available immediately. Refer to the Response codes section for all possible statuses.
Validate payment response against order details
Before confirming the order to the customer, validate that the payment response matches the original order details. This validation might include:
- Order ID
- Amount
- Product ID or SKU
- Currency
This validation ensures that the payment authorisation corresponds to the correct product and prevents mismatches due to potential manipulation or technical issues.
If the validation fails, do not confirm the order. Instead, initiate an investigation or notify the customer.
Testing
The sandbox environment accepts test payment parameters.
Find test values for credit cards and bank accounts in the Testing guide.
Move to production
-
Contact support to get live credentials.
-
Set the server type to
LIVEwhen initialisingOPPPaymentProvider.self.provider = [OPPPaymentProvider paymentProviderWithMode:OPPProviderModeLive];let provider = OPPPaymentProvider(mode: OPPProviderMode.live) -
Update the backend to use the correct API endpoints and credentials.
Android
For Android devices the below guide provides information on using ready-to-use payment forms.
Requirements
Install the Mobile SDK
-
Add the Mobile SDK for Android.
-
Drag
oppwa.mobile.aar&ipworks3ds_sdk.aar(useipworks3ds_sdk_deploy.aarfor production) to thelibsfolder of the module where you plan to integrate the Mobile SDK. -
In the
build.gradlefile of this module, add:implementation fileTree(dir: "libs", include: ["*.aar"]) -
Add the required dependencies to your
build.gradle. Refer to thedependencies.txtprovided in the release. It contains the list of required dependencies for the Mobile SDK. -
Enable the
viewBindingfeature:buildFeatures { viewBinding true }
Set up your server
To start working with the SDK, expose two APIs on your backend for your Android app to communicate with:
- Endpoint 1: Creating a checkout ID
- Endpoint 2: Getting the result of payment
Refer to the set up your server guide for detailed instructions.
Request Checkout ID
Your app should request a checkout ID from your server. This example uses the sample integration server; adapt it to your own backend API.
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
}Present Checkout UI
1. Configure the checkout settings
Define the settings for the checkout screen. Initialise CheckoutSettings with the received checkout ID. It controls the information displayed to the shopper. For the first integration, enable synchronous payment methods. Other payment brands need extra configuration.
Set<String> paymentBrands = new LinkedHashSet<String>();
paymentBrands.add("VISA");
paymentBrands.add("MASTER");
paymentBrands.add("DIRECTDEBIT_SEPA");
CheckoutSettings checkoutSettings = new CheckoutSettings(checkoutId, paymentBrands, Connect.ProviderMode.TEST);
// since mSDK version 6.0.0, the shopper result URL is not required
checkoutSettings.setShopperResultUrl("companyname://result");val paymentBrands = hashSetOf("VISA", "MASTER", "DIRECTDEBIT_SEPA")
val checkoutSettings = CheckoutSettings(checkoutId, paymentBrands, Connect.ProviderMode.TEST)
// since mSDK version 6.0.0 the shopper result URL is not required
checkoutSettings.shopperResultUrl = "companyname://result"2. Present the checkout page
Start the checkout activity and handle the result.
public class MainActivity extends AppCompatActivity {
private final ActivityResultLauncher checkoutLauncher = registerForActivityResult(
new CheckoutActivityResultContract(),
this::handleCheckoutResult
);
private void startCheckout() {
checkoutLauncher.launch(checkoutSettings);
}
private void handleCheckoutResult(@NonNull CheckoutActivityResult result) {
if (result.isCanceled()) {
// shopper cancelled the checkout process
return;
}
String resourcePath = result.getResourcePath();
if (resourcePath != null) {
// get the payment status using the resourcePath
}
}
}class MainActivity : AppCompatActivity() {
private val checkoutLauncher = registerForActivityResult(CheckoutActivityResultContract()) {
result: CheckoutActivityResult -> handleCheckoutResult(result)
}
private fun startCheckout() {
checkoutLauncher.launch(checkoutSettings)
}
private fun handleCheckoutResult(result: CheckoutActivityResult) {
if (result.isCanceled) {
// shopper cancelled the checkout process
return
}
val resourcePath = result.resourcePath
if (resourcePath != null) {
// get the payment status using the resourcePath
}
}
}You can use the
getErrorInfo()method ofPaymentErrorto get more details about the error.
Get the payment status
Your app should request the payment status from your server for any result except cancelled.
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
}The transaction status may not be available immediately. To support all statuses, refer to the response codes documentation.
Validate payment response against order details
Before confirming the order to the customer, validate that the payment response matches the original order details. This validation might include:
- Order ID
- Amount
- Product ID or SKU
- Currency
This validation ensures that the payment authorisation corresponds to the correct product and prevents mismatches due to potential manipulation or technical issues.
If the validation fails, do not confirm the order. Instead, initiate an investigation or notify the customer.
Testing
The sandbox environment accepts test payment parameters. You can find test values for credit cards and bank accounts in the testing guide.
Go to production
-
Contact support to get the live credentials.
-
Adjust the server type to
LIVEin your initialisation ofCheckoutSettings.CheckoutSettings checkoutSettings = new CheckoutSettings(checkoutId, paymentBrands, Connect.ProviderMode.LIVE);val checkoutSettings = CheckoutSettings(checkoutId, paymentBrand, Connect.ProviderMode.LIVE) -
Change your backend to use the correct API endpoints and credentials.
Updated about 7 hours ago