# MTI API Integration
source: https://developer.mastercard.com/mastercard-threat-intelligence/documentation/tutorials-and-guides/api-sandbox-guide-tutorial/index.md

## Overview {#overview}

This tutorial shows you how to build a small Java application that consumes the Mastercard Threat Intelligence (MTI) API. You start in the Sandbox environment, call each of the three main features, and print the responses.

The goal is a working reference you can build in minutes. The code follows the KISS principle, so it stays short and easy to read.

*** ** * ** ***

### About the Mastercard Threat Intelligence (MTI) API {#about-the-mastercard-threat-intelligence-mti-api}

Mastercard Threat Intelligence provides payment-focused cyber threat intelligence. It helps issuers and acquirers detect, monitor, and respond to emerging fraud and ecosystem threats.

The API gives you programmatic access to this intelligence through REST endpoints. You send a request, and the API returns structured JavaScript Object Notation (JSON) results.

*** ** * ** ***

### What You Build {#what-you-build}

By the end of this tutorial, you have a Maven project that:

* Authenticates with Mastercard using OAuth 2.0.
* Generates a type-safe API client from the OpenAPI specification.
* Calls each of the three features and prints the results.

*** ** * ** ***

### The Three MTI Features {#the-three-mti-features}

The API groups its endpoints into three features. Each feature supports independent use cases, so you can adopt one or all three.

|                Feature                |                                   Purpose                                   |               Primary Users                |
|---------------------------------------|-----------------------------------------------------------------------------|--------------------------------------------|
| Merchant Threat Intelligence          | Assess the risk of a merchant web domain using fraud and cyber indicators.  | Acquirers, merchant risk teams             |
| Payment Ecosystem Threat Intelligence | Find high-risk vulnerabilities that affect payment and e-commerce software. | Issuers, acquirers, cybersecurity teams    |
| Card Testing                          | Investigate card testing activity, merchant behaviors, and transactions.    | Issuers, acquirers, fraud operations teams |

You call each feature in a dedicated step later in this tutorial.

*** ** * ** ***

### Glossary {#glossary}

This tutorial uses the following terms:

* **BIN**: Bank Identification Number.
* **CID**: Company ID.
* **CVE**: Common Vulnerabilities and Exposures, a unique identifier for a known vulnerability.
* **DPoP**: Demonstrating Proof of Possession, a security mechanism that binds an access token to a key.
* **ICA**: Interbank Card Association number.
* **OAuth 2.0**: The authorization framework used to obtain an access token.

*** ** * ** ***

## Step 1 - Prerequisites {#step-1---prerequisites}

Before you start, make sure you have the tools and access described below.

*** ** * ** ***

### Tools {#tools}

You need the following tools installed:

* Java Development Kit (JDK) 17 or later.
* Apache Maven 3.6 or later.
* Visual Studio Code, with the Extension Pack for Java.

To confirm your versions, run these commands:

```bash
java -version
mvn -version
```

*** ** * ** ***

### Knowledge {#knowledge}

This tutorial assumes you are comfortable with the following:

* Java and Maven basics.
* REST APIs and JSON.
* OpenAPI specifications.

*** ** * ** ***

### Mastercard Access {#mastercard-access}

You need a Mastercard Developers project with the Mastercard Threat Intelligence API enabled. From that project, gather these items:

* Your client ID.
* Your key ID.
* Your signing key, as a PKCS#12 keystore file, along with its password and key alias.

You add these values to your configuration in a later step.

*** ** * ** ***

### The OpenAPI Specification {#the-openapi-specification}

This tutorial uses the Mastercard Threat Intelligence OpenAPI specification file -
[mastercard-threat-intelligence-api.yaml](https://static.developer.mastercard.com/content/mastercard-threat-intelligence/swagger/mastercard-threat-intelligence-api.yaml) (66KB)

*** ** * ** ***

## Step 2 - Create the Project {#step-2---create-the-project}

In this step, you create a new Maven project in Visual Studio Code.

*** ** * ** ***

### Create the Project Folder {#create-the-project-folder}

Create a folder named `mti-api-demo` for your project. Open it in Visual Studio Code with **File \> Open Folder**.

*** ** * ** ***

### Choose the Maven Coordinates {#choose-the-maven-coordinates}

Maven identifies your project with three coordinates. This tutorial uses the following values:

* `groupId`: `com.example.mti`
* `artifactId`: `mti-api-demo`
* `version`: `1.0.0`

The base Java package matches the group ID: `com.example.mti`.

*** ** * ** ***

### Create the Folder Structure {#create-the-folder-structure}

Create the standard Maven folder structure inside your project:

```text
mti-api-demo/
├── pom.xml
└── src/
    └── main/
        ├── java/
        │   └── com/example/mti/
        └── resources/
```

You add the `pom.xml` file in the dependencies step. You add your Java classes under `src/main/java/com/example/mti/` and your configuration files under `src/main/resources/`.

The next step covers the resources you place in `src/main/resources/`.

*** ** * ** ***

## Step 3 - Set Up Project Resources {#step-3---set-up-project-resources}

In this step, you add three resources to `src/main/resources/`: the OpenAPI specification, your signing key, and a configuration file.

*** ** * ** ***

### Add the OpenAPI Specification {#add-the-openapi-specification}

Copy the
[mastercard-threat-intelligence-api.yaml](https://static.developer.mastercard.com/content/mastercard-threat-intelligence/swagger/mastercard-threat-intelligence-api.yaml) (66KB) file into `src/main/resources/`. The build uses this file to generate the API client in a later step.

*** ** * ** ***

### Add the Signing Key {#add-the-signing-key}

Copy your PKCS#12 keystore into `src/main/resources/`. This tutorial expects a file named `mti-api-signing.p12`.
Alert: Keeping the key on the classpath makes the demo easy to run. For a real application, store the key outside the code and load it from a secure location.

*** ** * ** ***

### Add the Configuration File {#add-the-configuration-file}

Create a file named `application.properties` in `src/main/resources/`. This file holds all environment settings, so you can switch environments without changing code.

Add the following content. Replace each placeholder with your own value:

```properties
# Base URL for the MTI API
mti.api.base-url=https://sandbox.cyber.mastercard.com

# OAuth 2.0 token endpoint and issuer
mti.oauth.token-url=https://sandbox.api.mastercard.com/oauth/token
mti.oauth.issuer=https://sandbox.api.mastercard.com

# Client credentials
mti.oauth.client-id=YOUR_CLIENT_ID
mti.oauth.key-id=YOUR_KEY_ID

# Signing key (PKCS#12 keystore on the classpath)
mti.oauth.keystore=mti-api-signing.p12
mti.oauth.keystore-password=YOUR_KEYSTORE_PASSWORD
mti.oauth.key-alias=YOUR_KEY_ALIAS

# Access scopes (space-separated)
mti.oauth.scopes=mc-threat-intelligence:card-testing_read mc-threat-intelligence:domains_read mc-threat-intelligence:vulnerabilities_read
```

*** ** * ** ***

### Understand the Settings {#understand-the-settings}

The table below describes each setting:

|            Setting            |                    Description                    |
|-------------------------------|---------------------------------------------------|
| `mti.api.base-url`            | The base URL for the API environment.             |
| `mti.oauth.token-url`         | The OAuth 2.0 endpoint that issues access tokens. |
| `mti.oauth.issuer`            | The token issuer that matches the environment.    |
| `mti.oauth.client-id`         | Your client ID from Mastercard Developers.        |
| `mti.oauth.key-id`            | The key ID that identifies your signing key.      |
| `mti.oauth.keystore`          | The keystore file name on the classpath.          |
| `mti.oauth.keystore-password` | The password for the keystore.                    |
| `mti.oauth.key-alias`         | The alias of the key inside the keystore.         |
| `mti.oauth.scopes`            | The access scopes granted to your project.        |

You reference these settings from your Java code in a later step.

*** ** * ** ***

## Step 4 - Add Project Dependencies {#step-4---add-project-dependencies}

In this step, you create the `pom.xml` file. It declares your dependencies and the build plugins.

*** ** * ** ***

### Key Dependencies {#key-dependencies}

The project uses a small set of libraries:

* The Mastercard OAuth 2.0 client, which handles authentication. For more information, read the [oauth2-client-java](https://github.com/Mastercard/oauth2-client-java) documentation.
* OkHttp and Gson, which the generated API client uses for HTTP and JSON.

*** ** * ** ***

### Key Plugins {#key-plugins}

The build uses two plugins:

* The OpenAPI Generator plugin, which creates the API client from the specification.
* The Exec plugin, which runs the application from the command line.

*** ** * ** ***

### Create the pom.xml {#create-the-pomxml}

Add the following `pom.xml` to your project root:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example.mti</groupId>
    <artifactId>mti-api-demo</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <name>Mastercard Threat Intelligence API Demo</name>
    <description>A simple demo that consumes the Mastercard Threat Intelligence API.</description>

    <properties>
        <maven.compiler.release>17</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <!-- Library versions -->
        <oauth2-client.version>1.0.3</oauth2-client.version>
        <okhttp.version>4.12.0</okhttp.version>
        <gson.version>2.11.0</gson.version>
        <gson-fire.version>1.9.0</gson-fire.version>
        <jakarta-annotation.version>2.1.1</jakarta-annotation.version>
        <slf4j.version>2.0.13</slf4j.version>

        <!-- Plugin versions -->
        <compiler-plugin.version>3.13.0</compiler-plugin.version>
        <openapi-generator.version>7.11.0</openapi-generator.version>
        <exec-plugin.version>3.5.0</exec-plugin.version>
    </properties>

    <dependencies>
        <!-- Mastercard OAuth 2.0 client: handles authentication for Mastercard APIs -->
        <dependency>
            <groupId>com.mastercard.developer</groupId>
            <artifactId>oauth2-client-java</artifactId>
            <version>${oauth2-client.version}</version>
        </dependency>

        <!-- HTTP client and JSON library used by the generated API client -->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>${okhttp.version}</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>logging-interceptor</artifactId>
            <version>${okhttp.version}</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>${gson.version}</version>
        </dependency>
        <dependency>
            <groupId>io.gsonfire</groupId>
            <artifactId>gson-fire</artifactId>
            <version>${gson-fire.version}</version>
        </dependency>
        <dependency>
            <groupId>jakarta.annotation</groupId>
            <artifactId>jakarta.annotation-api</artifactId>
            <version>${jakarta-annotation.version}</version>
        </dependency>

        <!-- Optional: shows OAuth 2.0 library logs on the console -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>${slf4j.version}</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- Compiles the project with the configured Java release -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${compiler-plugin.version}</version>
            </plugin>

            <!-- Generates the API client from the OpenAPI spec during the build -->
            <plugin>
                <groupId>org.openapitools</groupId>
                <artifactId>openapi-generator-maven-plugin</artifactId>
                <version>${openapi-generator.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <inputSpec>${project.basedir}/src/main/resources/mastercard-threat-intelligence-api.yaml</inputSpec>
                            <generatorName>java</generatorName>
                            <library>okhttp-gson</library>
                            <apiPackage>com.example.mti.client.api</apiPackage>
                            <modelPackage>com.example.mti.client.model</modelPackage>
                            <invokerPackage>com.example.mti.client.invoker</invokerPackage>
                            <generateApiTests>false</generateApiTests>
                            <generateModelTests>false</generateModelTests>
                            <generateApiDocumentation>false</generateApiDocumentation>
                            <generateModelDocumentation>false</generateModelDocumentation>
                            <configOptions>
                                <dateLibrary>java8</dateLibrary>
                                <useJakartaEe>true</useJakartaEe>
                                <openApiNullable>false</openApiNullable>
                                <containerDefaultToNull>true</containerDefaultToNull>
                                <hideGenerationTimestamp>true</hideGenerationTimestamp>
                                <sourceFolder>src/main/java</sourceFolder>
                            </configOptions>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!-- Runs the application with: mvn exec:java -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>${exec-plugin.version}</version>
                <configuration>
                    <mainClass>com.example.mti.MtiDemoApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
```

The next step explains how the OpenAPI Generator plugin turns the specification into a Java client.

*** ** * ** ***

## Step 5 - Generate the API Client {#step-5---generate-the-api-client}

In this step, you generate a type-safe Java client from the OpenAPI specification. The OpenAPI Generator plugin does this during the build, so you never write the client by hand.

*** ** * ** ***

### Generate on the Fly {#generate-on-the-fly}

The plugin binds to the `generate-sources` phase. This means the client is created automatically before compilation whenever you build.

To generate the client without a full build, run:

```bash
mvn generate-sources
```

To generate the client and compile in one step, run:

```bash
mvn compile
```

The generated code lands under `target/generated-sources/openapi/`. The build adds this folder to the source path, so your code can use the client directly.

*** ** * ** ***

### Understand the Generator Options {#understand-the-generator-options}

The `pom.xml` from the previous step sets these options:

|          Option          |              Value               |                                     Purpose                                      |
|--------------------------|----------------------------------|----------------------------------------------------------------------------------|
| `generatorName`          | `java`                           | Generates a Java client.                                                         |
| `library`                | `okhttp-gson`                    | Uses OkHttp for HTTP and Gson for JSON.                                          |
| `apiPackage`             | `com.example.mti.client.api`     | Package for the API classes.                                                     |
| `modelPackage`           | `com.example.mti.client.model`   | Package for the request and response models.                                     |
| `invokerPackage`         | `com.example.mti.client.invoker` | Package for the client infrastructure.                                           |
| `dateLibrary`            | `java8`                          | Uses `java.time` types for dates.                                                |
| `useJakartaEe`           | `true`                           | Uses Jakarta annotations, which suit Java 17.                                    |
| `containerDefaultToNull` | `true`                           | Defaults unset arrays and maps to `null` so the client omits them from requests. |

*** ** * ** ***

### Review the Generated Classes {#review-the-generated-classes}

The generator creates one API class for each feature, named after its tag:

* `MerchantThreatIntelligenceApi`
* `PaymentEcosystemThreatIntelligenceApi`
* `CardTestingApi`

It also creates the `ApiClient` and `ApiException` classes in the invoker package, plus a model class for each request and response.

You wire these classes together in the next step.

*** ** * ** ***

## Step 6 - Authenticate and Build the API Client {#step-6---authenticate-and-build-the-api-client}

In this step, you write two classes. `AppConfig` reads your settings. `MtiClientFactory` authenticates and builds a ready-to-use API client.

*** ** * ** ***

### How Authentication Works {#how-authentication-works}

The Mastercard Threat Intelligence API uses OAuth 2.0. The Mastercard OAuth 2.0 client library does the work for you:

1. It signs a request with your private key to prove your identity.
2. It exchanges that request for a short-lived access token.
3. It attaches the token to every API call.

The library adds this behavior through an OkHttp interceptor, so your API calls stay clean.

*** ** * ** ***

### Read the Configuration {#read-the-configuration}

Create `AppConfig.java` under `src/main/java/com/example/mti/`. This class loads `application.properties` and exposes each setting through a method:

```java
package com.example.mti;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/**
 * Loads demo settings from application.properties on the classpath.
 */
public class AppConfig {

    private final Properties props = new Properties();

    public AppConfig() {
        this("application.properties");
    }

    public AppConfig(String resourceName) {
        try (InputStream in = getClass().getClassLoader().getResourceAsStream(resourceName)) {
            if (in == null) {
                throw new IllegalStateException("Configuration file not found: " + resourceName);
            }
            props.load(in);
        } catch (IOException e) {
            throw new UncheckedIOException("Failed to read configuration: " + resourceName, e);
        }
    }

    public String get(String key) {
        String value = props.getProperty(key);
        if (value == null || value.isBlank()) {
            throw new IllegalStateException("Missing required setting: " + key);
        }
        return value.trim();
    }

    public String getOrDefault(String key, String fallback) {
        String value = props.getProperty(key);
        return (value == null || value.isBlank()) ? fallback : value.trim();
    }

    public String baseUrl() {
        return get("mti.api.base-url");
    }

    public String tokenUrl() {
        return get("mti.oauth.token-url");
    }

    public String issuer() {
        return get("mti.oauth.issuer");
    }

    public String clientId() {
        return get("mti.oauth.client-id");
    }

    public String keyId() {
        return get("mti.oauth.key-id");
    }

    public String keystore() {
        return get("mti.oauth.keystore");
    }

    public String keystorePassword() {
        return get("mti.oauth.keystore-password");
    }

    public String keyAlias() {
        return get("mti.oauth.key-alias");
    }

    public Set<String> scopes() {
        Set<String> scopes = new LinkedHashSet<>();
        for (String scope : get("mti.oauth.scopes").split("\\s+")) {
            if (!scope.isBlank()) {
                scopes.add(scope.trim());
            }
        }
        return scopes;
    }

    public Map<String, String> additionalHeaders() {
        Map<String, String> headers = new LinkedHashMap<>();
        for (String entry : getOrDefault("mti.api.additional-headers", "").split(",")) {
            int separator = entry.indexOf(':');
            if (separator > 0) {
                headers.put(entry.substring(0, separator).trim(), entry.substring(separator + 1).trim());
            }
        }
        return headers;
    }
}
```

*** ** * ** ***

### Build the API Client {#build-the-api-client}

Create `MtiClientFactory.java` under `src/main/java/com/example/mti/`. This class loads your signing key, configures OAuth 2.0, and returns a configured `ApiClient`:

```java
package com.example.mti;

import com.example.mti.client.invoker.ApiClient;
import com.mastercard.developer.oauth2.config.OAuth2Config;
import com.mastercard.developer.oauth2.config.SecurityProfile;
import com.mastercard.developer.oauth2.core.access_token.InMemoryAccessTokenStore;
import com.mastercard.developer.oauth2.core.dpop.StaticDPoPKeyProvider;
import com.mastercard.developer.oauth2.core.scope.StaticScopeResolver;
import com.mastercard.developer.oauth2.http.okhttp3.OAuth2Interceptor;
import com.mastercard.developer.oauth2.keys.KeyGenerator;
import okhttp3.OkHttpClient;

import java.io.InputStream;
import java.net.URL;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.time.Duration;
import java.util.Map;

/**
 * Builds an API client that authenticates with Mastercard using OAuth 2.0.
 */
public class MtiClientFactory {

    private final AppConfig config;

    public MtiClientFactory(AppConfig config) {
        this.config = config;
    }

    public ApiClient create() throws Exception {
        PrivateKey clientKey = loadClientKey();
        KeyPair dpopKeyPair = KeyGenerator.generateEcKeyPair("secp256r1");

        OAuth2Config oauthConfig = OAuth2Config.builder()
                .securityProfile(SecurityProfile.FAPI2SP_PRIVATE_KEY_DPOP)
                .clientId(config.clientId())
                .tokenEndpoint(new URL(config.tokenUrl()))
                .issuer(new URL(config.issuer()))
                .clientKey(clientKey)
                .kid(config.keyId())
                .accessTokenStore(new InMemoryAccessTokenStore())
                .scopeResolver(new StaticScopeResolver(config.scopes()))
                .dpopKeyProvider(new StaticDPoPKeyProvider(dpopKeyPair))
                .clockSkewTolerance(Duration.ofSeconds(10))
                .build();

        OkHttpClient baseClient = new OkHttpClient.Builder().build();
        OkHttpClient httpClient = new OkHttpClient.Builder()
                .addInterceptor(new OAuth2Interceptor(oauthConfig, baseClient.newBuilder()))
                .build();

        ApiClient apiClient = new ApiClient(httpClient);
        apiClient.setBasePath(config.baseUrl());
        for (Map.Entry<String, String> header : config.additionalHeaders().entrySet()) {
            apiClient.addDefaultHeader(header.getKey(), header.getValue());
        }
        return apiClient;
    }

    private PrivateKey loadClientKey() throws Exception {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        try (InputStream in = getClass().getClassLoader().getResourceAsStream(config.keystore())) {
            if (in == null) {
                throw new IllegalStateException("Signing key not found: " + config.keystore());
            }
            keyStore.load(in, config.keystorePassword().toCharArray());
        }
        return (PrivateKey) keyStore.getKey(config.keyAlias(), config.keystorePassword().toCharArray());
    }
}
```

*** ** * ** ***

### What the Factory Does {#what-the-factory-does}

The `create` method performs four tasks:

* It loads your private key from the PKCS#12 keystore.
* It builds an `OAuth2Config` from your settings.
* It registers the `OAuth2Interceptor`, which adds a token to every request.
* It returns an `ApiClient` with the correct base URL.

With the client ready, you can call each feature. The next three steps do exactly that.

*** ** * ** ***

## Step 7 - Consume Merchant Threat Intelligence {#step-7---consume-merchant-threat-intelligence}

Merchant Threat Intelligence helps you assess the risk of a merchant web domain. You submit one or more domains with a reason for the inquiry. The API returns risk indicators grouped into categories, such as malware, phishing, and payment fraud.

*** ** * ** ***

### When to Use It {#when-to-use-it}

You can use this feature to support these workflows:

* Screening a merchant domain during onboarding or underwriting.
* Investigating a suspicious domain reported by a fraud team.
* Monitoring a domain in your portfolio over time.

*** ** * ** ***

### The Endpoint {#the-endpoint}

This feature uses one endpoint:

* `domainIntelligenceSearch` sends a POST request to `/exposures/domains`.

You send a `reason` and a list of `domains`. The `reason` must be one of `MERCHANT_UNDERWRITING_CONCERNS`, `COLLUSIVE_MERCHANT`, or `SCAM_MERCHANT`.

*** ** * ** ***

### The Code {#the-code}

Add this method to your application. It searches one domain and prints the report:

```java
/** Merchant Threat Intelligence: assess the risk of a merchant domain. */
static void runMerchantThreatIntelligence(ApiClient apiClient) {
    System.out.println("== Merchant Threat Intelligence ==");
    MerchantThreatIntelligenceApi api = new MerchantThreatIntelligenceApi(apiClient);
    DomainSearch request = new DomainSearch()
            .reason(DomainSearch.ReasonEnum.SCAM_MERCHANT)
            .domains(List.of("test.com"));
    try {
        System.out.println(api.domainIntelligenceSearch(request));
    } catch (ApiException e) {
        printError(e);
    }
    System.out.println();
}
```

*** ** * ** ***

### Read the Response {#read-the-response}

The response is a `DomainReportCollection`. Each report includes the domain, the number of risks found, and a list of categories. Each category holds individual risk findings with a severity color and a timestamp.

You assemble the full application, including the `printError` helper, in a later step.

*** ** * ** ***

## Step 8 - Consume Payment Ecosystem Threat Intelligence {#step-8---consume-payment-ecosystem-threat-intelligence}

Payment Ecosystem Threat Intelligence gives you visibility into high-risk vulnerabilities that affect payment and e-commerce software. The API returns Common Vulnerabilities and Exposures (CVE) records with risk scores, severity labels, and lifecycle phases.

*** ** * ** ***

### When to Use It {#when-to-use-it-1}

You can use this feature to support these workflows:

* Prioritizing remediation for the most dangerous vulnerabilities.
* Assessing exposure across the software in your payment ecosystem.
* Adding vulnerability context to a security investigation.

*** ** * ** ***

### The Endpoint {#the-endpoint-1}

This feature uses one endpoint:

* `petiVulnerabilities` sends a GET request to `/exposures/vulnerabilities`.

You shape the results with query parameters for free-text search, filters, sorting, and pagination. Every parameter is optional.

*** ** * ** ***

### The Code {#the-code-1}

Add this method to your application. It searches for vulnerabilities that match a text query and prints the first five results:

```java
/** Payment Ecosystem Threat Intelligence: search high-risk vulnerabilities. */
static void runPaymentEcosystemThreatIntelligence(ApiClient apiClient) {
    System.out.println("== Payment Ecosystem Threat Intelligence ==");
    PaymentEcosystemThreatIntelligenceApi api = new PaymentEcosystemThreatIntelligenceApi(apiClient);
    try {
        // Parameters: searchText, severityLabel, lifecyclePhase, limit, offset, sortBy, sortDirection
        System.out.println(api.petiVulnerabilities("SQL injection", null, null, 5, 0, null, null));
    } catch (ApiException e) {
        printError(e);
    }
    System.out.println();
}
```

*** ** * ** ***

### Read the Response {#read-the-response-1}

The response is a `VulnerabilityPage`. It holds a list of records and a `page` object with the limit, offset, and total. Each record includes the CVE ID, the risk score, the severity label, and a description.

You assemble the full application, including the `printError` helper, in a later step.

*** ** * ** ***

## Step 9 - Consume Card Testing {#step-9---consume-card-testing}

Card Testing intelligence helps you investigate card testing activity across the payment ecosystem. Card testing is when criminals run small authorization attempts to validate stolen card details before larger fraud.

*** ** * ** ***

### The Three Views {#the-three-views}

Card Testing exposes three complementary views:

|       View        |                        Purpose                        | Primary Users |
|-------------------|-------------------------------------------------------|---------------|
| Observed Testing  | Merchants confirmed to be used for card testing.      | Acquirers     |
| Testing Behaviors | Merchants that show behavior linked to card testing.  | Acquirers     |
| Transactions      | Individual authorizations identified as card testing. | Issuers       |

Issuers use the transactions view to find compromised cards. Acquirers use the observed testing and testing behaviors views to find affected merchants.

*** ** * ** ***

### The Search Endpoints {#the-search-endpoints}

Each view has its own search endpoint. All three accept a partial text search, field filters, sorting, and pagination:

* `observedTestingSearch` sends a POST request to `/threats/observed-testings/searches`.
* `testingBehaviorSearch` sends a POST request to `/threats/testing-behaviors/searches`.
* `transactionsSearch` sends a POST request to `/threats/transactions/searches`.

*** ** * ** ***

### The Filter Options Endpoint {#the-filter-options-endpoint}

Card Testing also offers an optional helper endpoint:

* `getFilterOptions` sends a GET request to `/threats/card-testings/filter-options`.

Both acquirers and issuers can call it. It returns the set of CIDs, ICAs, and BINs for which card testing data exists. You use these values to populate filters for the search endpoints.

The returned identifiers depend on the caller's role:

* An acquirer is identified by CID and ICA.
* An issuer is identified by CID, ICA, and BIN.

This endpoint is a convenience. You can search without it, but it helps you show only filter values that return data.

*** ** * ** ***

### The Code {#the-code-2}

Add this method to your application. It reads the filter options, then runs all three searches:

```java
/** Card Testing: investigate card testing activity across three views. */
static void runCardTesting(ApiClient apiClient) {
    System.out.println("== Card Testing ==");
    CardTestingApi api = new CardTestingApi(apiClient);

    try {
        System.out.println("Filter options (TRANSACTIONS): " + api.getFilterOptions("TRANSACTIONS"));
    } catch (ApiException e) {
        printError(e);
    }

    try {
        ObservedTestingSearch request = new ObservedTestingSearch()
                .searchText("Brooklyn")
                .page(new Pagination().limit(5).offset(0))
                .sort(new Sorting().by("cardTestingDate").direction(Sorting.DirectionEnum.DESC));
        System.out.println("Observed testing: " + api.observedTestingSearch(request));
    } catch (ApiException e) {
        printError(e);
    }

    try {
        TestingBehaviorSearch request = new TestingBehaviorSearch()
                .searchText("Brooklyn")
                .page(new Pagination().limit(5).offset(0))
                .sort(new Sorting().by("classificationDate").direction(Sorting.DirectionEnum.DESC));
        System.out.println("Testing behaviors: " + api.testingBehaviorSearch(request));
    } catch (ApiException e) {
        printError(e);
    }

    try {
        TransactionsSearch request = new TransactionsSearch()
                .filters(new TransactionFilters()
                        .fieldFilters(new TransactionFiltersFieldFilters()
                                .issuerIcas(List.of("900100001"))))
                .page(new Pagination().limit(5).offset(0))
                .sort(new Sorting().by("transactionDate").direction(Sorting.DirectionEnum.DESC));
        System.out.println("Transactions: " + api.transactionsSearch(request));
    } catch (ApiException e) {
        printError(e);
    }
    System.out.println();
}
```

The observed testing and testing behaviors searches use a free-text `searchText`. The transactions search instead uses a field filter. It narrows results to one issuer ICA with `issuerIcas`. You can find valid ICA values with the `getFilterOptions` call.

You set only the fields you want to filter on. The client omits the fields you leave unset, so the search is not over-constrained.

*** ** * ** ***

### Read the Response {#read-the-response-2}

Each search returns a page object with a list of records and pagination details. The `getFilterOptions` call returns a `Filters` object that holds the available `cids`, `icas`, and `bins`.

Your access scopes decide which views return data. A call outside your role can return an empty result or an access error, which the demo reports gracefully.

You assemble the full application, including the `printError` helper, in the next step.

*** ** * ** ***

## Step 10 - Full Application Code {#step-10---full-application-code}

This step gives you the complete `MtiDemoApplication` class. It brings together the three feature methods and adds the `main` method and the `printError` helper.

Create `MtiDemoApplication.java` under `src/main/java/com/example/mti/`, then copy the code below:

```java
package com.example.mti;

import com.example.mti.client.api.CardTestingApi;
import com.example.mti.client.api.MerchantThreatIntelligenceApi;
import com.example.mti.client.api.PaymentEcosystemThreatIntelligenceApi;
import com.example.mti.client.invoker.ApiClient;
import com.example.mti.client.invoker.ApiException;
import com.example.mti.client.model.DomainSearch;
import com.example.mti.client.model.ObservedTestingSearch;
import com.example.mti.client.model.Pagination;
import com.example.mti.client.model.Sorting;
import com.example.mti.client.model.TestingBehaviorSearch;
import com.example.mti.client.model.TransactionFilters;
import com.example.mti.client.model.TransactionFiltersFieldFilters;
import com.example.mti.client.model.TransactionsSearch;

import java.util.List;

/**
 * Runs a short demo against each Mastercard Threat Intelligence API feature
 * and prints the responses.
 */
public class MtiDemoApplication {

    public static void main(String[] args) throws Exception {
        AppConfig config = new AppConfig();
        ApiClient apiClient = new MtiClientFactory(config).create();

        System.out.println("Connecting to: " + config.baseUrl());
        System.out.println();

        runMerchantThreatIntelligence(apiClient);
        runPaymentEcosystemThreatIntelligence(apiClient);
        runCardTesting(apiClient);
    }

    /** Merchant Threat Intelligence: assess the risk of a merchant domain. */
    static void runMerchantThreatIntelligence(ApiClient apiClient) {
        System.out.println("== Merchant Threat Intelligence ==");
        MerchantThreatIntelligenceApi api = new MerchantThreatIntelligenceApi(apiClient);
        DomainSearch request = new DomainSearch()
                .reason(DomainSearch.ReasonEnum.SCAM_MERCHANT)
                .domains(List.of("test.com"));
        try {
            System.out.println(api.domainIntelligenceSearch(request));
        } catch (ApiException e) {
            printError(e);
        }
        System.out.println();
    }

    /** Payment Ecosystem Threat Intelligence: search high-risk vulnerabilities. */
    static void runPaymentEcosystemThreatIntelligence(ApiClient apiClient) {
        System.out.println("== Payment Ecosystem Threat Intelligence ==");
        PaymentEcosystemThreatIntelligenceApi api = new PaymentEcosystemThreatIntelligenceApi(apiClient);
        try {
            // Parameters: searchText, severityLabel, lifecyclePhase, limit, offset, sortBy, sortDirection
            System.out.println(api.petiVulnerabilities("SQL injection", null, null, 5, 0, null, null));
        } catch (ApiException e) {
            printError(e);
        }
        System.out.println();
    }

    /** Card Testing: investigate card testing activity across three views. */
    static void runCardTesting(ApiClient apiClient) {
        System.out.println("== Card Testing ==");
        CardTestingApi api = new CardTestingApi(apiClient);

        try {
            System.out.println("Filter options (TRANSACTIONS): " + api.getFilterOptions("TRANSACTIONS"));
        } catch (ApiException e) {
            printError(e);
        }

        try {
            ObservedTestingSearch request = new ObservedTestingSearch()
                    .searchText("Brooklyn")
                    .page(new Pagination().limit(5).offset(0))
                    .sort(new Sorting().by("cardTestingDate").direction(Sorting.DirectionEnum.DESC));
            System.out.println("Observed testing: " + api.observedTestingSearch(request));
        } catch (ApiException e) {
            printError(e);
        }

        try {
            TestingBehaviorSearch request = new TestingBehaviorSearch()
                    .searchText("Brooklyn")
                    .page(new Pagination().limit(5).offset(0))
                    .sort(new Sorting().by("classificationDate").direction(Sorting.DirectionEnum.DESC));
            System.out.println("Testing behaviors: " + api.testingBehaviorSearch(request));
        } catch (ApiException e) {
            printError(e);
        }

        try {
            TransactionsSearch request = new TransactionsSearch()
                    .filters(new TransactionFilters()
                            .fieldFilters(new TransactionFiltersFieldFilters()
                                    .issuerIcas(List.of("900100001"))))
                    .page(new Pagination().limit(5).offset(0))
                    .sort(new Sorting().by("transactionDate").direction(Sorting.DirectionEnum.DESC));
            System.out.println("Transactions: " + api.transactionsSearch(request));
        } catch (ApiException e) {
            printError(e);
        }
        System.out.println();
    }

    /** Prints a short, readable message when a request fails. */
    static void printError(ApiException e) {
        System.out.println("Request failed with HTTP status " + e.getCode());
        if (e.getResponseBody() != null && !e.getResponseBody().isBlank()) {
            System.out.println("Response: " + e.getResponseBody());
        }
    }
}
```

Your project now has three Java classes: `AppConfig`, `MtiClientFactory`, and `MtiDemoApplication`. The next step builds and runs the application.

*** ** * ** ***

## Step 11 - Build and Run the Application {#step-11---build-and-run-the-application}

In this step, you build the project and run the demo. A successful run confirms that authentication, the generated client, and all three features work together.

*** ** * ** ***

### Build the Project {#build-the-project}

From the project root, run:

```bash
mvn clean package
```

This command generates the API client, compiles your code, and packages the application. A successful build ends with `BUILD SUCCESS`.

*** ** * ** ***

### Run the Application {#run-the-application}

Run the demo with the Exec plugin:

```bash
mvn exec:java
```

The application connects to the API, calls each feature, and prints the responses.

*** ** * ** ***

### Review the Output {#review-the-output}

The output starts with the environment you connected to, then shows a section for each feature. The sample below is trimmed for readability:

```text
Connecting to: https://sandbox.cyber.mastercard.com

== Merchant Threat Intelligence ==
class DomainReportCollection {
    reports: [class DomainReport {
        domain: test.com
        risksFound: 0
        totalRisks: 9
        categories: [class RiskCategory {
            categoryName: No Risk Observed
            severityColor: GREEN
            ...
        }]
        status: PASSED
    }]
}

== Payment Ecosystem Threat Intelligence ==
class VulnerabilityPage {
    records: [class Vulnerability {
        cveId: CVE-2018-7600
        riskScore: 100
        severityLabel: Very Critical
        ...
    }]
    page: class PageDetail {
        limit: 5
        offset: 0
        total: 10
    }
}

== Card Testing ==
Filter options (TRANSACTIONS): class Filters {
    cids: [...]
    icas: [...]
    bins: [...]
}
Observed testing: class ObservedTestingPage {
    records: [...]
    page: class PageDetail { limit: 5, offset: 0, total: ... }
}
Testing behaviors: class TestingBehaviorPage { ... }
Transactions: class TransactionsPage { ... }
```

*** ** * ** ***

### Confirm Success {#confirm-success}

Your integration works when the output shows the following:

* A section header for each of the three features.
* A `DomainReportCollection` with a `status` of `PASSED`.
* A `VulnerabilityPage` with one or more records.
* A `Filters` object with lists of identifiers.

Some Card Testing views return data only for the role your project holds. An empty result there is normal and does not indicate a failure.

*** ** * ** ***

## Step 12 - Switch to Production {#step-12---switch-to-production}

When your integration is ready, you move from the Sandbox environment to Production. Because all environment values live in `application.properties`, you switch without changing any code.

*** ** * ** ***

### Update the Configuration {#update-the-configuration}

Change the three environment URLs and swap in your Production credentials. The table below shows the values for each environment:

|        Setting        |                     Sandbox                      |                Production                |
|-----------------------|--------------------------------------------------|------------------------------------------|
| `mti.api.base-url`    | `https://sandbox.cyber.mastercard.com`           | `https://cyber.mastercard.com`           |
| `mti.oauth.token-url` | `https://sandbox.api.mastercard.com/oauth/token` | `https://api.mastercard.com/oauth/token` |
| `mti.oauth.issuer`    | `https://sandbox.api.mastercard.com`             | `https://api.mastercard.com`             |

*** ** * ** ***

### Update the Credentials {#update-the-credentials}

Replace your Sandbox credentials with your Production credentials:

* Set `mti.oauth.client-id` and `mti.oauth.key-id` to your Production values.
* Replace the keystore file with your Production signing key.
* Update `mti.oauth.keystore-password` and `mti.oauth.key-alias` to match.

*** ** * ** ***

### Production Configuration Example {#production-configuration-example}

A Production `application.properties` looks like this:

```properties
# Base URL for the MTI API
mti.api.base-url=https://cyber.mastercard.com

# OAuth 2.0 token endpoint and issuer
mti.oauth.token-url=https://api.mastercard.com/oauth/token
mti.oauth.issuer=https://api.mastercard.com

# Client credentials
mti.oauth.client-id=YOUR_PRODUCTION_CLIENT_ID
mti.oauth.key-id=YOUR_PRODUCTION_KEY_ID

# Signing key (PKCS#12 keystore on the classpath)
mti.oauth.keystore=mti-api-signing.p12
mti.oauth.keystore-password=YOUR_KEYSTORE_PASSWORD
mti.oauth.key-alias=YOUR_KEY_ALIAS

# Access scopes (space-separated)
mti.oauth.scopes=mc-threat-intelligence:card-testing_read mc-threat-intelligence:domains_read mc-threat-intelligence:vulnerabilities_read
```

*** ** * ** ***

### Protect Your Credentials {#protect-your-credentials}

Production credentials are sensitive. Keep them out of source control. For a real deployment, load the keystore and secrets from a secure store or environment variables rather than from a file on the classpath.

You now have a working Mastercard Threat Intelligence integration that runs in both Sandbox and Production.

*** ** * ** ***

