# Using OpenAPI for Business Payment Service End to End
source: https://developer.mastercard.com/mastercard-supplier-payment-agent/documentation/tutorials_guides/tutorials/connect-to-the-bps-using-open-api/index.md

## Overview {#overview}

This tutorial explains how to generate a simple client application to consume the Business Payment Service API. This tutorial also includes steps to integrate with Mastercard's OAuth 1.0 authentication and authorization flow.

### You will learn how to {#you-will-learn-how-to}

* Generate a REST client from an OpenAPI/Swagger specification
* Sign requests using our OAuth1.0a signer libraries

### What you'll need {#what-youll-need}

Before starting this tutorial ensure that you have completed the following:

* Supplied your Mastercard representative with your Suppliers
* Created your project and retrieved the required credentials by following this [guide](https://developer.mastercard.com/mastercard-supplier-payment-agent/documentation/tutorials_guides/guides/impl-guide/index.md)
* Shared your consumer key with your Mastercard representative and received confirmation that you were onboarded to MTF

## Steps {#steps}

### 1. Download the Business Payment Service OpenAPI/Swagger File {#1-download-the-business-payment-service-openapiswagger-file}

Open the Supplier Payment Agent Swagger definition [here](https://developer.mastercard.com/mastercard-supplier-payment-agent/documentation/api-reference/index.md).

Download the swagger definition file in YAML format.

### 2. Generate the client {#2-generate-the-client}

Mastercard has provided a generic tutorial you can follow to generate an Open API client. Refer to
[Generating an Open API client](https://developer.mastercard.com/platform/documentation/generating-and-configuring-a-mastercard-api-client/).

The tutorial is based on Java but you can find further tutorials for all six supported languages on [Github](https://github.com/Mastercard/mastercard-api-client-tutorial).
For more information about swagger-codegen and more options, please refer to the official page on [GitHub](https://github.com/OpenAPITools/openapi-generator).

### 3. Set up your Maven Dependencies {#3-set-up-your-maven-dependencies}

Your pom.xml file will have two entries:
* Java

```Java
<dependency>
   <groupId>com.mastercard.developer</groupId>
   <artifactId>oauth1-signer</artifactId>
   <version>1.4.0</version>
</dependency>
<dependency>
   <groupId>com.mastercard.developer</groupId>
   <artifactId>client-encryption</artifactId>
   <version1.4.0</version>
</dependency>
<dependency>
   <groupId>org.openapitools</groupId>
   <artifactId>openapi-generator-cli</artifactId>
   <version>4.3.1</version>
</dependency>
<dependency>
   <groupId>com.squareup.okhttp3</groupId>
   <artifactId>okhttp</artifactId>
   <version>4.1.0</version>
</dependency>
<dependency>
   <groupId>com.google.code.gson</groupId>
   <artifactId>gson</artifactId>
   <version>2.8.4</version>
</dependency>
<dependency>
   <groupId>com.squareup.okhttp3</groupId>
   <artifactId>logging-interceptor</artifactId>
   <version>3.11.0</version>
</dependency>
<dependency>
   <groupId>io.gsonfire</groupId>
   <artifactId>gson-fire</artifactId>
   <version>1.8.0</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.11.1</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.11.1</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.30</version>
</dependency>
<build>
   <plugins>
<plugin>
   <groupId>org.openapitools</groupId>
   <artifactId>openapi-generator-maven-plugin</artifactId>
   <version>4.3.1</version>
   <executions>
      <execution>
         <goals>
            <goal>generate</goal>
         </goals>
         <configuration>
            <inputSpec>${project.basedir}/src/main/resources/track_bps_spa_service.yaml</inputSpec>
            <generatorName>java</generatorName>
            <library>okhttp-gson</library>
            <generateApiTests>false</generateApiTests>
            <generateModelTests>false</generateModelTests>
            <generateApiTests>false</generateApiTests>
            <configOptions>
               <sourceFolder>src/gen/java/main</sourceFolder>
               <groupId>com.mastercard.developer</groupId>
               <artifactId>track-bps-spa-client</artifactId>
               <invokerPackage>com.mastercard.developer.track-bps-spa-client</invokerPackage>
               <apiPackage>com.mastercard.developer.track-bps-spa-client.api</apiPackage>
               <modelPackage>com.mastercard.developer.track-bps-spa-client.model</modelPackage>
               <dateLibrary>java8</dateLibrary>
                 <java8>true</java8>
            </configOptions>
         </configuration>
      </execution>
   </executions>
</plugin>

   </plugins>
</build>
```

### 4. Configure and Call the Supplier Payment Agent APIs {#4-configure-and-call-the-supplier-payment-agent-apis}

#### Configurations Class {#configurations-class}

Update the configurations.
* Java

```Java
public class BPSConfig {

    public static String SERVICE_BASE_URL="https://sandbox.api.mastercard.com/track/bps";
    
    // OAUTH Signing Key configurations
    public static String SIGNING_P12_PATH = "./src/main/resources/<REPLACE-ME>.p12";
    public static String SIGNING_CONSUMER_KEY = "<REPLACE-ME>";
    public static String SIGNING_KEY_ALIAS = "<REPLACE-ME>";
    public static String SIGNING_KEY_STORE_PASSWORD = "<REPLACE-ME>";

    
    // Mastercard Encryption Key configuration (For Decrypting payload)
    public static String MASTERCARD_ENCRYPTION_P12_PATH="./src/main/resources/<REPLACE-ME>.p12";
    public static String MASTERCARD_ENCRYPTION_KEY_ALIAS="<REPLACE-ME>";
    public static String MASTERCARD_ENCRYPTION_KEY_STORE_PASSWORD="<REPLACE-ME>";

    // Configure CardPayment Request - paymentId
public static String PAYMENT_ID="<REPLACE-ME>";
    }
```

<br />

#### Call the API {#call-the-api}

Finally, the below code shows how to call the View Payment Instructions endpoint. If you have successfully connected it will return the payment instruction details.

Follow the code level comments to get more details on OAuth signing and encryption.
* Java

```Java
 public class SupplierPaymentAgentOpenApi {

    public static final Logger log = LoggerFactory.getLogger(SupplierPaymentAgentOpenApi.class);

    // Enabling or disabling encryption.
    private static boolean enableEncryption = true;

    // jackson ObjectMapper for conversion.
    private static ObjectMapper objectMapper = new ObjectMapper();

    public static void main(String[] args) {

        try {
            buildObjectMapper();

            // Get Payment-Instruction
            CardPaymentResponse cardPaymentResponse = getPaymentInstruction(BPSConfig.PAYMENT_ID);
            log.info(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(cardPaymentResponse));

        } catch (Exception ex) {
            handleException(ex);
        }
    }

    public static CardPaymentResponse getPaymentInstruction(String paymentId) throws Exception {
        SupplierPaymentAgentsApi api = new SupplierPaymentAgentsApi(signRequest(enableEncryption));
        ApiResponse<CardPaymentResponse> responseWithHttpInfo = api.cardSupplierPaymentGetWithHttpInfo(paymentId);
        return responseWithHttpInfo.getData();
    }

    public static ApiClient signRequest(Boolean enableEncryption) throws Exception {
        ApiClient client = new ApiClient();
        client.setBasePath(BPSConfig.SERVICE_BASE_URL);

        if(enableEncryption){
            client.setHttpClient(
                    client.getHttpClient()
                            .newBuilder()
                            .addInterceptor(new OkHttpFieldLevelEncryptionInterceptor(getEncryptionConfig()))
                            .addInterceptor(new OkHttpOAuth1Interceptor(BPSConfig.SIGNING_CONSUMER_KEY, getSigningKey()))
                            .build()
            );
        }else{
            client.setHttpClient(
                    client.getHttpClient()
                            .newBuilder()
                            .addInterceptor(new OkHttpOAuth1Interceptor(BPSConfig.SIGNING_CONSUMER_KEY, getSigningKey()))
                            .build()
            );
        }
        return client;
    }

    private static PrivateKey getSigningKey() throws Exception {
        return AuthenticationUtils.loadSigningKey(BPSConfig.SIGNING_P12_PATH,
                BPSConfig.SIGNING_KEY_ALIAS, BPSConfig.SIGNING_KEY_STORE_PASSWORD);
    }

    private static FieldLevelEncryptionConfig getEncryptionConfig() throws Exception {
        PrivateKey decryptionKey = EncryptionUtils.loadDecryptionKey(BPSConfig.MASTERCARD_ENCRYPTION_P12_PATH, BPSConfig.MASTERCARD_ENCRYPTION_KEY_ALIAS, BPSConfig.MASTERCARD_ENCRYPTION_KEY_STORE_PASSWORD);
        return FieldLevelEncryptionConfigBuilder.aFieldLevelEncryptionConfig()
                .withDecryptionKey(decryptionKey)
                .withDecryptionPath("$.encryptedCard", "$.card")
                .withOaepPaddingDigestAlgorithm("SHA-256")
                .withEncryptedValueFieldName("encryptedValue")
                .withEncryptedKeyFieldName("encryptedKey")
                .withIvFieldName("iv")
                .withOaepPaddingDigestAlgorithmFieldName("oaepPaddingDigestAlgorithm")
                .withEncryptionKeyFingerprintFieldName("publicKeyFingerprint")
                .withFieldValueEncoding(FieldLevelEncryptionConfig.FieldValueEncoding.BASE64)
                .build();
    }

    private static void buildObjectMapper() {
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
        objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
        objectMapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, false);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
    }

    public static void handleException(Exception ex) {
        if(ex instanceof ApiException){
            ApiException apiException = (ApiException) ex;
            Error error = gson.fromJson(apiException.getResponseBody(),Error.class);
            log.error(gson.toJson(error));
        }else {
            ErrorErrorsError standardError = getErrorErrorsError(ex);
            ErrorErrors standardErrorHeader = getErrorErrors(standardError);
            Error error = new Error();
            error.setErrors(standardErrorHeader);
            Gson gson = new GsonBuilder().setPrettyPrinting().create();
            log.error(gson.toJson(error));
        }
    }

    private static ErrorErrors getErrorErrors(ErrorErrorsError standardError) {
        List<ErrorErrorsError> errorList = new ArrayList<>();
        errorList.add(standardError);
        ErrorErrors standardErrorHeader = new ErrorErrors();
        standardErrorHeader.error(errorList);
        return standardErrorHeader;
    }

    private static ErrorErrorsError getErrorErrorsError(Exception ex) {
        ErrorErrorsError standardError = new ErrorErrorsError();
        standardError.setSource("REFERENCE_IMPLEMENTATION");
        standardError.setReasonCode("UNKNOWN");
        standardError.setDescription(ex.getMessage());
        standardError.setDetails(ExceptionUtils.getStackTrace(ex));
        standardError.setRecoverable(false);
        return standardError;
    }
}
```

To decrypt the payload:
* Java

```Java
// Encryption configuration.
    private static FieldLevelEncryptionConfig getEncryptionConfig() throws Exception {
        PrivateKey decryptionKey = EncryptionUtils.loadDecryptionKey(BPSConfig.MASTERCARD_ENCRYPTION_P12_PATH, BPSConfig.MASTERCARD_ENCRYPTION_KEY_ALIAS, BPSConfig.MASTERCARD_ENCRYPTION_KEY_STORE_PASSWORD);
        return FieldLevelEncryptionConfigBuilder.aFieldLevelEncryptionConfig()
                .withDecryptionKey(decryptionKey)
                .withDecryptionPath("$.encryptedCard", "$.card")
                .withOaepPaddingDigestAlgorithm("SHA-256")
                .withEncryptedValueFieldName("encryptedValue")
                .withEncryptedKeyFieldName("encryptedKey")
                .withIvFieldName("iv")
                .withOaepPaddingDigestAlgorithmFieldName("oaepPaddingDigestAlgorithm")
                .withEncryptionKeyFingerprintFieldName("publicKeyFingerprint")
                .withFieldValueEncoding(FieldLevelEncryptionConfig.FieldValueEncoding.BASE64)
                .build();
    }
```

To sign the payload using OAuth signer library, configure the following:
* JAVA

```JAVA
    public static ApiClient signRequest(Boolean enableEncryption) throws Exception {
        ApiClient client = new ApiClient();
        client.setBasePath(BPSConfig.SERVICE_BASE_URL);

        if(enableEncryption){
            client.setHttpClient(
                    client.getHttpClient()
                            .newBuilder()
                            .addInterceptor(new OkHttpFieldLevelEncryptionInterceptor(getEncryptionConfig()))
                            .addInterceptor(new OkHttpOAuth1Interceptor(BPSConfig.SIGNING_CONSUMER_KEY, getSigningKey()))
                            .build()
            );
        }else{
            client.setHttpClient(
                    client.getHttpClient()
                            .newBuilder()
                            .addInterceptor(new OkHttpOAuth1Interceptor(BPSConfig.SIGNING_CONSUMER_KEY, getSigningKey()))
                            .build()
            );
        }
        return client;
    }

    private static PrivateKey getSigningKey() throws Exception {
        return AuthenticationUtils.loadSigningKey(BPSConfig.SIGNING_P12_PATH,
                BPSConfig.SIGNING_KEY_ALIAS, BPSConfig.SIGNING_KEY_STORE_PASSWORD);
    }
```

##### View Payment Instructions Response {#view-payment-instructions-response}

At this point, you should have successfully integtated with Business Payment Service. If you submitted a View Payment Instructions Request your response could look something like this:
* JSON

```JSON
{
  "paymentId": "T-16F-AFA-34D-DDO-T7T-Z6D",
  "status": "RECEIVED",
  "description": "Description",
  "buyerId": "matest.buyer1.pay@track",
  "supplierId": "matest.supplier1@track",
  "totalAmount": 15000,
  "currency": "USD",
  "invoices": [
    {
      "referenceId": "INV-20192",
      "date": "2019-03-09",
      "amount": "20000",
      "adjustmentAmount": 5000,
      "adjustmentCode": "INCORRECT_AMOUNT",
      "customerNumber": "AX123"
    }
  ],
  "messages": [
   {
      "description": "Transaction T-16F-AFA-34D-DDO-T7T-Z6D has been flagged. The payment amount exceeds maximum allowed by this Supplier.",
      "message": "AMT_GRT_WARN"
    }],
  "card": {
    "number": "5123450000000008",
    "nameOnCard": "Lee Cardholder",
    "expMonth": "09",
    "expYear": "2032",
    "cvv": "293"
  },
  "createdDate": "2017-07-21T17:32:28Z",
  "updatedDate": "2017-07-21T17:32:28Z"
}
```

##### Post Update Payment Instructions Response {#post-update-payment-instructions-response}

If you submitted an Update Payment Instructions request, you should get a response similar to this:
* JSON

```JSON
{
  "messages": [
    {
      "description": "Transaction T-16F-AFA-34D-DDO-T7T-Z6D has been flagged. The payment amount exceeds maximum allowed by this Supplier.",
      "message": "AMT_GRT_WARN"
    }
  ],
  "paymentId": "T-16F-AFA-34D-DDO-T7T-Z6D",
  "status": "APPROVED",
  "createdDate": "2017-07-21T17:32:28.000Z",
  "updatedDate": "2017-07-21T17:32:28.000Z"
}
```

