TransactionPermit
The TransactionPermit SPI controls transaction authorization. It enables you to make final authorization decisions during the transaction process based on transaction details and risk control results.
Method signature
public interface TransactionPermit {
AppPermitResult isAllowed(boolean authBefore, @Nullable TransactionInfo transactionInfo, @NotNull RiskControlInfo riskControlInfo);
}Request parameters
Method | Item | Type | Description |
isAllowed | authBefore | boolean | Indicates whether the user completed authentication within 30 seconds before the transaction. Use this field to skip re-authentication for recent sessions. |
transactionInfo | Transaction information. Can be | ||
riskControlInfo | Risk control evaluation result. |
TransactionInfo
Method | Return type | Description |
getAuthorizedAmount() | byte[] | The transaction amount. |
getCurrencyCode() | byte[] | The transaction currency code. |
getCountryCode() | byte[] | The terminal country code. |
getOtherAmount() | byte[] | A second amount, if provided by the terminal. |
getRichTransactionType() | The transaction type, following ISO 8583. | |
getTransactionRange() | The transaction range, following the card scheme specification. | |
getExpectedUserActionOnPoi() | The action the terminal expects from the user at the point of interaction. | |
getPurpose() | Indicates whether the terminal settles the transaction immediately or defers it. | |
getConditionsOfUse() | Indicates whether the transaction is domestic or international. | |
hasTerminalRequestedCdCvm() | boolean | Returns |
isAlternateAidUsed() | boolean | Returns |
RichTransactionType
Value | Type | Description |
| ENUM | A standard purchase transaction. |
| ENUM | A cash withdrawal. |
| ENUM | A purchase transaction that includes cashback to the user. |
| ENUM | A refund transaction. Treat the amount as a credit rather than a deduction. |
| ENUM | A transportation-related transaction. |
TransactionRange
Value | Type | Description |
| ENUM | A low-value transaction. |
| ENUM | A high-value transaction. |
| ENUM | The transaction range could not be determined. |
ExpectedUserActionOnPoi
The action the terminal expects from the user at the point of interaction. Most actions listed below cannot be performed on a mobile device. This field is provided for informational purposes and does not imply that the MPP app must fulfill these actions.
Value | Type | Description |
| ENUM | No action is expected from the user. |
| ENUM | The terminal expects a PIN entry. |
| ENUM | The terminal expects a signature. |
| ENUM | The terminal accepts either a PIN or a signature. |
| ENUM | The expected action could not be determined. |
Purpose
Value | Type | Description |
| ENUM | The terminal settles the transaction immediately. |
| ENUM | The terminal records the tap and settles the transaction later. |
| ENUM | The settlement method could not be determined. |
ConditionsOfUse
Value | Type | Description |
| ENUM | The transaction occurs in the card's home country. |
| ENUM | The transaction occurs outside the card's home country.
|
| ENUM | The terminal did not provide location information. |
public interface TransactionInfo {
byte[] getCurrencyCode();
byte[] getCountryCode();
byte[] getAuthorizedAmount();
byte[] getOtherAmount();
TransactionRange getTransactionRange();
RichTransactionType getRichTransactionType();
ExpectedUserActionOnPoi getExpectedUserActionOnPoi();
Purpose getPurpose();
ConditionsOfUse getConditionsOfUse();
boolean hasTerminalRequestedCdCvm();
boolean isAlternateAidUsed();
}RiskControlInfo
Method | Item | Type | Description |
RiskControlInfo() | riskControlDecision | boolean | Indicates whether the risk control check passed. |
isRiskProceed() | N/A | boolean | Returns the value of riskControlDecision. |
isRiskCalculated() | N/A | boolean | Indicates whether the risk evaluation has been completed. |
getRiskAmountLevel() | N/A | String | The authentication-exempted amount level determined by risk control. |
setRiskControlDecision() | riskControlDecision | boolean | Updates the risk control decision. |
setRiskCalculated() | riskCalculated | boolean | Updates whether the risk evaluation has been completed. |
setRiskAmountLevel() | riskAmountLevel | String | Updates the authentication-exempted amount level. |
public RiskControlInfo(boolean riskControlDecision) {
this.riskControlDecision = riskControlDecision;
}
private boolean riskControlDecision;
public boolean isRiskProceed() {
return riskControlDecision;
}
public void setRiskControlDecision(boolean riskControlDecision) {
this.riskControlDecision = riskControlDecision;
}
private boolean isRiskCalculated = false;
public boolean isRiskCalculated() {
return isRiskCalculated;
}
public void setRiskCalculated(boolean riskCalculated) {
isRiskCalculated = riskCalculated;
}
private String riskAmountLevel = "";
public String getRiskAmountLevel() {
return riskAmountLevel;
}
public void setRiskAmountLevel(String riskAmountLevel) {
this.riskAmountLevel = riskAmountLevel;
}Response parameters
Item | Type | Description | Required |
result | The authorization decision for the transaction. | Yes |
AppPermitResult
Value | Type | Description |
| ENUM | The transaction is authorized. |
| ENUM | The transaction is declined. |
| ENUM | User authentication is required before the transaction can proceed. |
| ENUM | The transaction was declined for MPP-defined reason |
| ENUM | The transaction was declined for MPP-defined reason |
| ENUM | The transaction was declined for MPP-defined reason |
| ENUM | The transaction was declined for MPP-defined reason |
| ENUM | The transaction was declined for MPP-defined reason |
Note:
DECLINE_CUSTOM_1throughDECLINE_CUSTOM_5are values of the AppPermitResult enum. They are distinct from the NFCErrorCode constantsAPP_CUSTOM_DECLINE_1throughAPP_CUSTOM_DECLINE_5, which correspond to error codes11001through11005.
Sample
The following example shows a typical implementation of TransactionPermit. The MPP app returns PROCEED if the risk control check passed, or NEED_AUTH if additional user authentication is required.
public class TransactionAuthInProgress implements TransactionPermit {
private Context context;
private SharedPreferencesUtil sharedPreferencesUtil;
public static final String CURRENCY_CODE = "currencyCode";
public static final String AUTHORIZED_AMOUNT = "authorizedAmount";
private static final String IS_3TAB_PAGE = "is_3tab_page";
private static final String IS_3TAB_PAGE_Switch = "is_3tab_page_switch";
public TransactionAuthInProgress(Context context) {
this.context = context;
sharedPreferencesUtil = new SharedPreferencesUtil(context);
}
@Override
public AppPermitResult isAllowed(boolean authBefore, TransactionInfo transactionInfo, @NonNull RiskControlInfo riskControlInfo) {
Log.d("PERMIT", "authBefore: " + authBefore + ", riskControlInfo.isRiskProceed(): " + riskControlInfo.isRiskProceed());
byte[] currencyCode = transactionInfo.getCurrencyCode();
byte[] authorizedAmount = transactionInfo.getAuthorizedAmount();
Log.d("20241113", "currencyCode: " + currencyCode + ", authorizedAmount: " + authorizedAmount);
boolean is3tabSwitch = sharedPreferencesUtil.getBool(IS_3TAB_PAGE_Switch);
boolean is3TabPage = sharedPreferencesUtil.getBool(IS_3TAB_PAGE);
Log.d("20241231", "currencyIs3TabPage: " + is3TabPage );
if (currencyCode != null) {
String currencyCodeString = HCEApduCommandMock.t(currencyCode);
sharedPreferencesUtil.saveString(CURRENCY_CODE, currencyCodeString);
}
if (authorizedAmount != null) {
String authorizedAmountString = HCEApduCommandMock.t(authorizedAmount);
sharedPreferencesUtil.saveString(AUTHORIZED_AMOUNT, authorizedAmountString);
}
if (!is3tabSwitch || transactionInfo.getRichTransactionType() == RichTransactionType.TRANSIT) {
return (riskControlInfo.isRiskProceed())? AppPermitResult.PROCEED :AppPermitResult.NEED_AUTH;
} else {
if (is3TabPage) {
return AppPermitResult.PROCEED;
} else {
return AppPermitResult.DECLINE;
}
}
}
}