Push transactions to Subaio via Partner API

This document aims to help developers send transactions to Subaio via Partner API.

1. Introduction

Transactions are sent via POST /partner/{partnerId}/2.0.0/transactions

{
  "reqId": "27e2e489-205c-412e-bb41-e1b65ddf754e",
  "txs": "string"
}

Transactions are sent as a JWE (JSON Web Encryption) payload, where a JSON object is constructed containing a list of transactions and then encrypted and put into the txs payload.

Each API call should not contain more than 750 transactions in the txs payload. Large sets of transactions should thus be split into multiple API calls.

See API documentation for details on API

1.1 Historical transactions

When a user is onboarded, historical transactions should be sent to Subaio for processing. Typically, 25 months are sent to ensure at least two payments for yearly subscriptions are found.

The recognition engine needs to know when all historical transactions have been sent, so it can process and present a complete overview. This is achieved by adding a marker element in the last batch of historical transactions of type IMPORT_COMPLETED.

Batch 1 containing maximum 750 transactions

[
  { "uid": "user-1", "type": "TX", ..., "tx": { ... } },
  ...
  { "uid": "user-1", "type": "TX", ..., "tx": { ... } }
]

Batch 2 where a marker element of type IMPORT_COMPLETED is added.

[
  { "uid": "user-1", "type": "TX", ..., "tx": { ... } },
  ...
  { "uid": "user-1", "type": "TX", ..., "tx": { ... } }
  { "uid": "user-1", "type": "IMPORT_COMPLETED" }
]

1.2 Daily or live updates

New transactions can either be sent on as a daily job, or throughout the day.

You are strongly encouraged to batch transactions together to avoid sending single transactions and limit associated overhead on both client and server side.

Batches can contain transactions from multiple users.

2. Implementation

Here follows an example implementation in java for generating the JSON request body with the JWE payload.

var requestId = UUID.randomUUID().toString();

var request = new TransactionsRequestDTO();
request.setReqId(requestId);

var transactions = buildTransactionList(userId, ...);

var jwePayload = encryptTransactions(transactions);
request.setTxs(jwePayload);

return request;

2.1 Building Transaction List

List<TransactionDTO> buildTransactionList(String userId, ...) {
  var transactions = new List<TransactionDTO>();

  // TODO: Map internal format to Subaio Partner API format
  ...

  // Add IMPORT_COMPLETED to end of list to signal no more data is coming.
  transactions.add(TransactionDTO.builder()
                                 .uid(userId)
                                 .type(TransactionImportType.IMPORT_COMPLETED)
                                 .build()
  );

  return transactions;
}

2.2 Generating JWE Payload

Generating a JWE payload requires us first to convert the java objects into a stringified JSON using an ObjectMapper. Here we declare an ObjectMapper for this specific purpose to ensure all values are serialized correctly.

public class TransactionObjectMapperContainer {
    private final static ObjectMapper OBJECT_MAPPER =
            new ObjectMapper()
                    .registerModule(new JavaTimeModule())
                    .setSerializationInclusion(JsonInclude.Include.NON_NULL)
                    .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                    .disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
                    .enable(SerializationFeature.WRITE_DATES_WITH_ZONE_ID)
                    .enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)
                    .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

    public static ObjectMapper objectMapper() {
        return OBJECT_MAPPER;
    }
}

The second step is to encrypt the string using JWE. Attached to this guide is an example class JWE.java that performs the encryption.

private String encryptTransactions(List<TransactionDTO> transactions) {
  var json = TransactionObjectMapperContainer.objectMapper().writeValueAsString(transactions);
  var jwe = JWE.encrypt(transactions, PUBLIC_KEY);
  return jwe;
}

Files

Name
JWE.java