Protecting in-app payments

In this how-to, you will learn how to integrate the Incognia Transaction API to recognize trusted customers, increase conversions and reduce fraud for in-app mobile payments. You will be able to automatically assess the risk of payment events and review or decline suspicious ones.

Requirements

  • An Incognia account. Sign up here if you don't have an account.

  • An app with the Incognia SDK initialized. Follow SDK getting started guide for this.

  • API credentials to be able to make requests to Incognia APIs.

Step-by-step

Setting the User ID on the app

In order to verify a payment attempt, Incognia needs to link a device and a unique account identifier. The Account ID is used for this purpose.

This association must happen in two moments:

1. Whenever the application initializes

This covers the scenario where the application is updated with the Incognia SDK and the user is already logged in. The Application.onCreate() on Android and the [AppDelegate application:didFinishLaunchingWithOptions:] on iOS are good places to do this.

If the Account Id is not available in those locations, it is important to forward the value to the Incognia SDK as soon as it is available. The important action here is to call setUserId whenever the application initializes so it is guaranteed that we always know that a specific user is associated with a specific device.

2. Whenever the user logs in and out

It is necessary to call the setUserId method when the user finishes the login and clearUserId when the user logs out. This is usually done in the callback of these two actions.

Kotlin
Java
Swift
Objective-C
Kotlin
Incognia.setUserId(context, <userId>)
Java
Incognia.setUserId(context, <userId>);
Swift
ICGIncognia.setUserId(<userId>)
Objective-C
[ICGIncognia setUserId:<userId>];

While data such as e-mail and phone numbers are examples of identifiers, their use in the SDK is forbidden. Consider using non-personal information, such as UUIDs, and Hashing to securely generate and set the User ID.

Forwarding the device's Installation ID to your server

The Incognia SDK collects location and device data to build a behavioral profile for mobile users. This data is tagged with an identifier called Installation ID, which is automatically generated by the SDK.

To verify a payment attempt, Incognia needs to receive an Installation ID to identify the device from which the payment originates. Since your server will request the Incognia API to assess the risk of this payment, it needs to receive this information from your mobile application.

The installation ID can be forwarded to your server in two ways.

Option 1: Sending the Installation ID as a header

Before sending a payment request from your mobile application to your server, call Incognia.getInstallationId and set its value as a header of the request. We encourage you to choose a clear name for this header, like Incognia-Installation-ID, so you can easily identify it on the server-side.

This option has a clear benefit if your application will use more than one Incognia solution because you won't need to change each request's properties, like signup, login, payment, password change, etc.

Kotlin
Java
Swift
Objective-C
Kotlin
Incognia.getInstallationId(context) { result ->
if (result.isSuccessful) {
val installationId = result.result
// HttpURLConnection
httpUrlConnection.setRequestProperty("Incognia-Installation-ID", installationId)
}
// Send the request with the installationId to your backend server
}
Java
Incognia.getInstallationId(context, new IncogniaListener<String>() {
@Override
public void onResult(final Result<String> result) {
if (result.isSuccessful()) {
String installationId = result.getResult();
// HttpURLConnection
httpUrlConnection.setRequestProperty("Incognia-Installation-ID", installationId);
}
// Send the request with the installationId to your backend server
}
});
Swift
ICGIncognia.getInstallationId({ installationId in
if installationId != nil {
// NSURLRequest
request.setValue(
installationId,
forHTTPHeaderField: "Incognia-Installation-ID"
)
}
// Send the request with the installationId to your backend server
})
Objective-C
[ICGIncognia getInstallationId:^(NSString *installationId) {
if (installationId != nil) {
// NSURLRequest
[request setValue:installationId forHTTPHeaderField:@"Incognia-Installation-ID"];
}
// Send the request with the installationId to your backend server
}];

Option 2: Sending the Installation ID in the body of the request

Before sending the payment request from your mobile application to your server, call Incognia.getInstallationId and send it as additional information about this payment. We suggest that you choose a clear name for this property like Incognia-Installation-ID, so you can easily identify it on the server-side.

Handling the user's payment request

When your server receives a payment request, you can use Incognia intelligence to assess the risk of this new transaction inside this request/response cycle.

To evaluate a payment, your server needs to request our Transaction API informing that a payment was made, alongside its Installation ID, the Account ID, and the relevant addresses of the transaction, such as the home, billing, or shipping addresses.

Even though the addresses are an optional parameter, the risk assessment quality is greatly enhanced by its addition.

A sample implementation of a controller/handler

Let's consider a toy example as back-end with the controller below:

Ruby/Rails
Ruby/Rails
# orders_controller.rb
class OrdersController < ApplicationController
def create
@order_form = CreateOrderForm.new(params)
if @order_form.submit
redirect_to @order_form, notice: "Order completed!"
else
render action: :new
end
end
end
# create_order_form.rb
class CreateOrderForm < BaseForm
attr_accessor :user, :products, :payment_info, :total
# Some validations...
def submit
return nil if invalid?
Order.create(
user: user,
products: products.map(&:id),
total: total,
payment_authorization: process_payment
)
end
private
def process_payment
# Payment gateway logic
end
end

You are required to authenticate when using the Incognia API. For authentication details, see Authenticating in Incognia APIs.

Considering that the authentication logic is implemented, you can add risk assessment requests to your login handler:

Ruby/Rails
Ruby/Rails
# login_form.rb
class LoginForm < BaseForm
attr_accessor :user, :products, :payment_info, :total,
:incognia_installation_id
# Some validations...
validate :payment_risk
def submit
return nil if invalid?
Order.create(
user: user,
products: products.map(&:id),
total: total,
payment_authorization: process_payment
)
end
private
def process_payment
# Payment gateway logic
end
def payment_risk
return unless user
api = Incognia::Api.instance
risk_assessment = api.register_payment(
installation_id: incognia_installation_id,
account_id: user.id,
billing_address: build_address(payment_info[:billing_address])
)
# Automatically denies if Incognia gives high risk!
if risk_assessment == 'high_risk'
errors.add(:incognia_installation_id, 'considered unsafe!')
end
end
end
# incognia/api.rb
require 'faraday'
require 'json'
module Incognia
class Api
include Singleton
API_HOST = 'https://api.us.incognia.com/api/'.freeze
def register_payment(installation_id:, account_id:, billing_address:)
transactions_endpoint = 'v2/authentication/transactions'
params = {
installation_id: installation_id,
account_id: account_id,
type: 'payment',
# You can use billing, shipping and home types of address,
# this example only considers the usage of shipping.
addresses: [
{ type: :billing, structured_address: billing_address }
]
}
response = Faraday.post(
"#{API_HOST}#{transactions_endpoint}",
params.to_json,
headers
)
if response.status == 200
parsed_body = JSON.parse(response.body)
parsed_body['risk_assessment']
else
# Error handling
end
end
private
def headers
{
'Content-Type': 'application/json',
# Read more about how to generate a fresh token at our
# Authenticating in Incognia APIs section.
Authorization: "Bearer #{fresh_token}"
}
end
end
end

Using the Incognia risk assessment

When your server makes a request to the Transaction API endpoint, it will receive a response like the following:

{
"evidence": {
"account_integrity": {...},
"addresses": [...],
"device_behavior_reputation": "allowed",
"device_fraud_reputation": "allowed",
"device_integrity": {
"emulator": false,
"from_official_store": true,
"gps_spoofing": false,
"probable_root": false
},
"device_model": "SM-G9600",
"distance_to_trusted_location": 4.213,
"known_account": true,
"last_location_ts": "2019-08-24T14:15:22Z",
"location_events_quantity": 10,
"location_services": {
"location_permission_enabled": true,
"location_sensors_enabled": true
},
"sensor_match_type": "gps"
},
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"risk_assessment": "low_risk"
}

The response contains the id of the created entity (in this case, a payment), the risk_assessment provided by Incognia based on device behavior, and evidence that supports this assessment. You can learn more about all the returned data in this article: Understanding risk assessments.

The returned assessment can be used with other risk-related data, such as in a risk engine, to decide if this payment should be accepted. Incognia's risk assessments include:

  • high_risk: this payment may be fraudulent and we advise that preventive actions are taken in these scenarios, such as a manual review or a decline;

  • low_risk: this payment seems to be safe to accept;

  • unknown_risk: we are unable to provide a precise assessment at the time of the request. Location data for this installation and device might not be available at the moment.

Wrapping Up

After these steps, your application is ready to increase conversions and reduce fraud for in-app mobile payments.