The Flow
Table of contents
Participants
The diagram below introduces the participants of the flow as well as their interactions. Note, the blue boxes represent the services/components that merchant is responsible to maintain while the white ones are provided by mSIGNIA - the cAPI Server, as well a normal 3DS Server provided by a 3DS Server vendor:
click here to open a bigger image
This diagram is referenced in other sections of the documentation so it is recommended to keep it around.
Merchant mobile/web application
This assumes that a merchant has one or more apps be it mobile apps or web sites. For mobile apps the merchant uses Android or iOS uSDK while for web sites the uSDK Browser is used.
uSDK 7.X
This is the uSDK the merchant downloads and integrates into its mobile and/or web applications.
cAPI / Split SDK Server
This server-side component is essentially a companion to the uSDK 7.X. The uSDK and the cAPI / Split SDK Server
work as a whole abstracting the merchant from authentication protocols complexities. mSIGNIA usually provides this component to its uSDK customers. Please contact mSIGNIA administration for deployment options of the cAPI / Split SDK Server
component.
Merchant Backend
This is a server-side component that merchant runs as a backend system. The cAPI / Split SDK Server
calls it out in two cases:
- When it exchanges transaction data with the merchant - the
Exchange Transaction Data
callout - When it notified the merchant about transaction completion - the
Transaction Result
callout
3DS Server
This is a usual 3DS Server that actually kicks off a 3DS transaction by forming an AReq
and sending it over to a Directory Server.
The Callouts
There’re a couple of numbered callouts on the diagram, this section details them out.
Callout #1 - The Authenticate
method
The uSDK has a public method called authenticate
. This method is called by the merchant mobile or web application. Prior to that, the uSDK needs to be initialized as documented in the Obtaining and Installing section.
The authenticate
method expects two arguments, the AuthenticateSpec
as well as the AuthenticateCallback
:
usdk.authenticate(authenticateSpec, authenticateCallback);
AuthenticateSpec
This argument is used to let merchant convey some configuration data for the uSDK to perform authentication:
Field | Platform(s) | Required | Description |
---|---|---|---|
activity | Android | YES | A referece to an Android Activity |
iframeRef | Browser | YES | The iFrame element reference for browser uSDK to operate within. Merchant Merchant app is responsible to creating an iFrame of certain size and make it visible before calling authenticate |
viewController | iOS | YES | A reference to a UIViewController |
userId | All | NO | User identifier, can be an email or any other ID |
cardId | All | YES | A card number identifier. This must NOT be the actual number. It has to be either a unique ID or four last digist of the actual one |
orderId | All | YES | An identifier of the order within merchant’s database. The uSDK sends it over to exhangeTransactionDetails and transactionResult on the merchant backend so it is possible to reconcile the order/purchase being authenticated. |
splitSdkServerUrl | All | YES | The URL for cAPI / SplitSDK Server for uSDK to use |
exchangeTransactionDetailsUrl | All | YES | The URL of exchangeTransactionDetails endpoint on merchant backend |
transactionResultUrl | All | YES | The URL of transactionResult endpoint on merchant backend |
threeDsData | All | NO | Additional 3DS related data passed from app. It will be conveyed to merchant backend via exchangeTransactionDetailsUrl call. See ThreeDsData Elements below for more details |
merchantAuthInfo | All | NO | An additional information that merchant would use to authenticate itself to SplitSDK Server. This is a way to convey an API KEY or stuff like that to SplitSDK Server backend. |
AuthenticateCallback
This is a callback for uSDK to call when the authenticate
method finishes. It is an interface with the following signature (a java/Android code snippet is used):
public interface AuthenticateCallback {
void authenticated(AuthenticationResult authenticationResult);
void notAuthenticated(AuthenticationResult authenticationResult);
void decoupledAuthBeingPerformed(AuthenticationResult authenticationResult);
void cancelled(AuthenticationResult authenticationResult);
void error(AuthenticationResult authenticationResult);
}
The method names are self-descriptive. When authentication succeeds the uSDK calls the authenticated
method back, when user cancelled a challenge flow, the uSDK calls cancelled
and so on and so forth.
The AuthenticationResult
should be inspected to get more details about the actual result. Here’s AuthenticationResult
definition:
// Android
public class AuthenticationResult {
private String splitSdkServerTransID;
private String threeDSServerTransID;
private TransStatus status;
private String cardholderInfo;
private ShellSdkException error;
}
// Browser (typescript)
export interface AuthenticationResult {
splitSdkServerTransID?: string;
threeDSServerTransID?: string;
status?: string;
cardholderInfo?: string;
errorDescription?: string;
errorCode?: string;
errorDetails?: string
}
// iOS (Swift)
public struct AuthenticationResult: Decodable {
public let threeDSServerTransID: String?
public let splitSdkServerTransID: String?
public let status: TransStatus?
public let cardholderInfo: String?
public let error: AuthenticationResult.Error?
public struct Error: Decodable {
public let errorCode: String
public let errorDescription: String
public let errorDetails: String?
}
}
Complete Example (Android)
AuthenticateSpec authSpec = new AuthenticateSpec();
authSpec.setActivity(this);
authSpec.setUserId("pavlo-elrosado-demo@msignia.com");
authSpec.setCardId("4533");
authSpec.setOrderId("420e1eea-84d3-4f74-8c11-776cec65a047");
authSpec.setSplitSdkServerUrl("https://my-split-sdk-server.com/split-sdk-client/v1");
authSpec.setExchangeTransactionDetailsUrl("https://my-merchant.com/exchange-transaction-details");
authSpec.setTransactionResultUrl("https://my-merchant.com/transaction-result");
// OPTIONAL. More 3DS data can be passed to the uSDK.
// It then will be conveyed back to the `exchangeTransactionDetailsUrl` of merchant backend
authSpec.setThreeDsData(new ThreeDsData()
.acctID("96645-12-3456")
.threeDSRequestorAppURL("https://merchant.app.com/oob-callback")
// other data elements may go here
);
this.threeDS2Service.authenticate(authSpec, new AuthenticateCallback() {
@Override
public void authenticated(AuthenticationResult authenticationResult) {
Log.i("APP", "authenticated: " + authenticationResult);
}
@Override
public void notAuthenticated(AuthenticationResult authenticationResult) {
Log.i("APP", "notAuthenticated: " + authenticationResult);
}
@Override
public void decoupledAuthBeingPerformed(AuthenticationResult authenticationResult) {
Log.i("APP", "decoupledAuthBeingPerformed: " + authenticationResult);
}
@Override
public void cancelled(AuthenticationResult authenticationResult) {
Log.i("APP", "cancelled: " + authenticationResult);
}
@Override
public void error(AuthenticationResult authenticationResult) {
Log.i("APP", "error: " + authenticationResult);
}
});
Complete Example (Browser)
const authenticateSpec = {
userId: "pavlo-elrosado-demo@msignia.com",
cardId: "4533",
orderId: "420e1eea-84d3-4f74-8c11-776cec65a047",
exchangeTransactionDetailsUrl: "https://my-merchant.com/exchange-transaction-details",
transactionResultUrl: "https://my-merchant.com/transaction-result",
iframeRef: document.getElementById('3ds-iframe'),
splitSdkServerUrl: "https://my-split-sdk-server.com/split-sdk-client/v1",
// OPTIONAL. More 3DS data can be passed to the uSDK.
// It then will be conveyed back to the `exchangeTransactionDetailsUrl` of merchant backend
threeDsData: new ThreeDsData()
.acctID("96645-12-3456")
.threeDSRequestorAppURL("https://merchant.app.com/oob-callback")
// other data elements may go here
};
const authenticateCallback = {
authenticated: function (authenticationResult) {
console.log('authenticated successfully! authenticationResult: ', authenticationResult);
},
notAuthenticated: function (authenticationResult) {
console.log('not authenticated! authenticationResult: ', authenticationResult);
},
decoupledAuthBeingPerformed: function (authenticationResult) {
console.log('decoupled authentication being performed! authenticationResult: ', authenticationResult);
},
cancelled: function (authenticationResult) {
console.log('authentication cancelled! authenticationResult: ', authenticationResult);
},
error: function (authenticationResult) {
console.log('authentication erred! authenticationResult ', authenticationResult);
}
};
usdk.authenticate(authenticateSpec, authenticateCallback);
Note that for Browser, the merchant application has to provide a reference to an iFrame for the uSDK to work with. The iframe lifecycle is maintained by the merchant, so that is its responsibility to make the iframe right size, visible before authenticate
method called as well as hide it when the callback from the uSDK is received. A sample iframe definition would look like this:
<iframe
name="my-iframe"
sandbox="allow-forms allow-scripts allow-same-origin allow-pointer-lock"
style="border: 1px lightgray dashed; width: 600px; height: 400px;"></iframe>
Complete Example (iOS)
@IBAction func authenticate(_ sender: Any) {
do {
let authSpec = AuthenticateSpec(
viewController: self,
cardId: "420e1eea-84d3-4f74-8c11-776cec65a047",
orderId: "4533",
exchangeTransactionDetailsUrl: "https://my-merchant.com/exchange-transaction-details",
transactionResultUrl: "https://my-merchant.com/transaction-result",
splitSdkServerUrl: "https://my-split-sdk-server.com/split-sdk-client/v1",
userId: "pavlo-elrosado-demo@msignia.com",
// OPTIONAL. More 3DS data can be passed to the uSDK.
// It then will be conveyed back to the `exchangeTransactionDetailsUrl` of merchant backend
threeDsData: ThreeDsData()
.acctID("96645-12-3456")
.threeDSRequestorAppURL("https://merchant.app.com/oob-callback")
// other data elements may go here
)
try threeDS2Service.authenticate(spec: authSpec) { result in
switch result {
case .success(let authResult):
switch authResult {
case .authenticated(let authenticationResult):
print("authenticated: \(authenticationResult)")
case .notAuthenticated(let authenticationResult):
print("notAuthenticated: \(authenticationResult)")
case .cancelled(let authenticationResult):
print("cancelled: \(authenticationResult)")
case .decoupledAuthBeingPerformed(let authenticationResult):
print("decoupledAuthBeingPerformed: \(authenticationResult)")
case .error(let authenticationResult):
print("notAuthenticated: \(authenticationResult)")
}
case.failure(let error):
print("erred: \(error)")
}
}
}
catch {
print("An exception caught during `authenticate` call: \(error)")
}
}
Callout #2 - The Exhange Transaction Details
request
The cAPI / SplitSDK Server
sends an HTTP request to merchant backend using the URL provided to the uSDK via AuthenticateSpec.exchangeTransactionDetailsUrl
field. The details of this request are these:
- URL: uses
AuthenticateSpec.exchangeTransactionDetailsUrl
field value - Method:
POST
- Content-type:
application/json
- Body - a JSON document like shown below
{
v: 1,
orderId: "420e1eea-84d3-4f74-8c11-776cec65a047",
splitSdkServerTransId: "75f6f8ab-794e-4e24-95f2-3aee188a4408",
// an object with device attributes as per 3DS specification
threeDsData: {
deviceInfo: { },
acctID: "96645-12-3456"
}
userId: "john.doe@gmail.com"
}
Callout #3 - The Exhange Transaction Details
response
Merchant Backend is replying to the Exhange Transaction Details request with a datum whose structure is shown below. Note the threeDsData
data element, here the merchant should provide all necessary transaction and configuration information for the SplitSDK Server to form an initial request to send to a 3DS Server.
{
v: 1,
authentication: "3DS",
threeDsData: {
acctNumber: "532223423444234",
deviceInfo: { },
acctID: "96645-12-3456",
threeDSRequestorChallengeInd: "03",
threeDSRequestorID: "MC-99304",
// and so on
},
}
If merchant backend encounters an error its response is supposed to have non 2XX status code (40X, 50X are usually a good choice) in the following form:
{
payload: {
message: "Error message: i.e. Bad request received, a valid ID expected"
}
}
ThreeDsData elements
The threeDsData
data element in the response should contain all required elements shown in the table below.
Data Element | Required? | Platform | Description | Sample/Format |
---|---|---|---|---|
threeDSRequestorAppURL | N | APP | Merchant app declaring their URL within the CReq message so that the Authentication app can call the Merchant app after OOB authentication has occurred. | String. Variable, maximum 256 characters 2 characters. |
threeDSRequestorAuthenticationInd | Y | APP, BRW | Indicates the type of Authentication request. | 2 characters. 01 = Payment transaction, 02 = Recurring transaction, 03 = Instalment transaction, 04 = Add card, 05 = Maintain card, 06 = Cardholder verification as part of EMV token ID&V |
threeDSRequestorAuthenticationInfo | N | APP, BRW | ||
threeDSRequestorChallengeInd | N | APP, BRW | ||
threeDSRequestorID (**) | Y | APP, BRW, 3RI | DS assigned 3DS Requestor identifier. | |
threeDSRequestorName (**) | Y | APP, BRW, 3RI | DS assigned 3DS Requestor name. | |
threeDSRequestorPriorAuthenticationInfo | N | APP, BRW, 3RI | ||
threeDSRequestorURL | Y | APP, BRW, 3RI | Fully qualified URL of 3DS Requestor website or customer care site. | String. Variable, maximum 2048 characters |
threeRIInd | Y | 3RI | Indicates the type of 3RI request. Not applicable for APP nor BRW | |
acctType | N | APP, BRW, 3RI | ||
acquirerBIN (**) | Y | APP, BRW, 3RI | Acquiring institution identification code as assigned by the DS receiving the AReq message | |
acquirerMerchantID (**) | Y | APP, BRW, 3RI | Acquirer-assigned Merchant identifier. | |
addrMatch | N | APP, BRW | Indicates whether the Cardholder Shipping Address and Cardholder Billing Address are the same. | Length: 1 character. Y = match, N = not match |
cardExpiryDate | Y | APP, BRW, 3RI | Expiry Date of the PAN or token supplied to the 3DS Requestor by the Cardholder. | Format: YYMM |
acctInfo | N | APP, BRW, 3RI | ||
acctNumber | Y | APP, BRW, 3RI | Account number that will be used in the authorisation request for payment transactions. | String. Variable, 13–19 characters |
acctID | N | APP, BRW, 3RI | ||
billAddrCity | Y | APP, BRW, 3RI | Variable, maximum 50 characters | |
billAddrCountry | Y | APP, BRW, 3RI | 3 characters (ISO 3166-1) | |
billAddrLine1 | Y | APP, BRW, 3RI | Variable, maximum 50 characters | |
billAddrLine2 | Y | APP, BRW, 3RI | Variable, maximum 50 characters | |
billAddrLine3 | Y | APP, BRW, 3RI | Variable, maximum 50 characters | |
billAddrPostCode | Y | APP, BRW, 3RI | Variable, maximum 16 characters | |
billAddrState | Y | APP, BRW, 3RI | Should be the country subdivision code defined in ISO 3166-2. | 3 characters. |
deviceInfo | Y | APP | At minimum the merchant is supposed to return back the deviceInfo data element it received in Callout #2 | |
email | Y | APP, BRW, 3RI | Variable, maximum 254 characters | |
homePhone | N | APP, BRW, 3RI | The home phone number provided by the Cardholder. | See ThreeDsData section for more details |
mobilePhone | N | APP, BRW, 3RI | The mobile phone number provided by the Cardholder. | See ThreeDsData section for more details |
cardholderName | Y | APP, BRW, 3RI | Name of the Cardholder. | Variable, 2–45 characters |
shipAddrCity | Y | APP, BRW, 3RI | Variable, maximum 50 characters | |
shipAddrCountry | Y | APP, BRW, 3RI | 3 characters (ISO 3166-1) | |
shipAddrLine1 | Y | APP, BRW, 3RI | Variable, maximum 50 characters | |
shipAddrLine2 | Y | APP, BRW, 3RI | Variable, maximum 50 characters | |
shipAddrLine3 | Y | APP, BRW, 3RI | Variable, maximum 50 characters | |
shipAddrPostCode | Y | APP, BRW, 3RI | Variable, maximum 16 characters | |
shipAddrState | Y | APP, BRW, 3RI | Should be the country subdivision code defined in ISO 3166-2. | 3 characters. |
workPhone | Y if available | APP, BRW, 3RI | ||
purchaseInstalData | Y if available | APP, BRW, 3RI | Indicates the maximum number of authorisations permitted for instalment payments. Only for installments payments | |
mcc (**) | Y | APP, BRW, 3RI | DS-specific code describing the Merchant’s type of business, product or service. | 4 characters |
merchantCountryCode (**) | Y | APP, BRW, 3RI | Country Code of the Merchant. | 3 characters |
merchantName (**) | Y | APP, BRW, 3RI | Merchant name assigned by the Acquirer or Payment System | Variable, maximum 40 characters |
merchantRiskIndicator | N | APP, BRW, 3RI | ||
messageCategory | Y | APP, BRW, 3RI | Identifies the category of the message for a specific use case. | 2 characters. 01 = Payment, 02 = Non payment |
purchaseAmount | Y | APP, BRW, 3RI | Purchase amount in minor units of currency with all punctuation removed. | Variable, maximum 48 characters. Examples: 12345 |
purchaseCurrency | Y | APP, BRW, 3RI | Currency in which purchase amount is expressed. | Numeric. 3 characters. See ISO 4217 |
purchaseExponent | Y | APP, BRW, 3RI | Minor units of currency as specified in the ISO 4217 currency exponent. | String. 1 character. ISO 4217 |
purchaseDate | Y | APP, BRW, 3RI | Date and time of the purchase expressed in UTC. | Format: YYYYMMDDHHMMSS |
recurringExpiry | Y if recurring | APP, BRW, 3RI | Date after which no further authorisations shall be performed. | Format: YYYYMMDD |
recurringFrequency | Y if recurring | APP, BRW, 3RI | Indicates the minimum number of days between authorisations. | Variable, maximum 4 characters |
transType | N | APP, BRW, 3RI | Identifies the type of transaction being authenticated. | 2 characters. 01 = Goods/ Service Purchase, 03 = Check Acceptance, 10 = Account Funding, 11 = Quasi-Cash Transaction, 28 = Prepaid Activation and Load |
whiteListStatus | N | APP, BRW, 3RI | ||
whiteListStatusSource | N | APP, BRW, 3RI |
Callout #4 - The Transaction Result
request
When done authenticating the user, the cAPI / SplitSDK Server
sends a request to merchant backend notifying it about the fact. Here’re the details of the request:
- URL: uses
AuthenticateSpec.transactionResultUrl
field value - Method:
POST
- Content-type:
application/json
- Body - a JSON document like shown below.
{
threeDSServerTransID: "420e1eea-84d3-4f74-8c11-776cec65a047",
dsTransID: "b986150f-ed28-4fa1-bf3e-b4f17dc81dab",
acsTransID: "01e59f17-cb9c-4c17-967b-6f1ed9cbea98",
splitSdkServerTransId: "75f6f8ab-794e-4e24-95f2-3aee188a4408",
transStatus: "Y",
transStatusReason: "01", // may not present for certain transStatuses
transStatusReasonInfo: "some string here", // may not present for certain transStatuses
authenticationValue: "XXX", // may not present for certain transStatuses
eci: "YYY" // may not present for certain transStatuses
}
Callout #5 - The Transaction Result
response
Merchant, up on receiving the transaction result request is supposed to reply with any 200 response. A response with no body or with empty body or empty JSON document (i.e. a {}
) is okay.