# Overview

Lifecycle API The main purpose of a web service is to add a user and payment cards in a safe way to dedicated database PCI DSS compliant systems, which are provided by Verestro. By registering in the database, the partner can use various services provided by the Verestro company, e.g. Card Issuing, Card tokenization, Money Transfers and more. The API allows to mass import of data files to Verestro systems, such as: user, user with a card, or cards. Using the HTTPS REST protocol, you can add a new resource (user or cards), update their status, e.g. lock, unlock or completely remove a resource from system.

LIfecycle API is an internal service secured by x509 certificate, which increases the safety of transported data. The API communicates with the data storehouse called DataCore. DataCore is internal service and one of crucial components of Verestro's product line-up. Its main responsibility is to provide secure, PCI-DSS compliant storage for cardholder data. DataCore manages the status of the user and their aggregates. All other product in implementation connect to DataCore which returns information about the user and his aggregates.

## Security

### Data Storage

Lifecycle API is a part of PCI zone of Verestro platform so it meets all standards and restrictions of secure data storage. All fragile data is secured and encrypted.

We are using <span class="g-type-body-small">HashiCorp Vault as a Software HSM.</span>

The master key isn't stored anywhere. It is reconstructed in unsealing process. It is used to encrypt encryption key. The data stored by Vault is stored encrypted. Therefore, to decrypt the data, Vault must decrypt the encryption key which requires the master key. Unsealing is the process of reconstructing this master key.

Instead of distributing this master key as a single key to an operator, Vault uses an algorithm known as [Shamir's Secret Sharing](https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing) to split the key into key shards. A certain threshold of shards is required to reconstruct the master key.

[More information](https://www.vaultproject.io/docs/concepts/seal.html)

When the Vault is initialized it generates an encryption key which is used to protect all the data. The encryption key is also stored with the data, but encrypted with another encryption key known as the *master key*. Once Vault retrieves the encryption key, it is able to decrypt the data in the storage backend, and enters the *unsealed state.* Vault uses 256-bit AES to encrypt Encryption Key.

All sensitive data is encrypted in this way.

### Encryption of fragile data

LC api allows to encrypt fragile card data. Detailed description is provided below.

### JWE Standard

To encrypt fragile card data you should use JWE. If you are unfamiliar with this kind of standard please look at links below:  
[Wiki](https://en.wikipedia.org/wiki/JSON_Web_Encryption "Wiki"),  
[RFC](https://tools.ietf.org/html/rfc7516),  
[Example](https://web-token.spomky-labs.com/).

The setup for Lifecycle JWE is presented below:

- in headers: 
    - alg: RSA-OAEP-256 - keyEncryptionAlgo,
    - enc: A128GCM/A256GCM - contentEncryptionAlgo,
    - zip: DEF,
    - iat: this field should contain current timestamp,
    - kid: SHA1 of thumbprint of public key used to generate JWE (Static values is: Pdk08OtjTS6-I7H\_E96XKme0BOY),
- in body: plaintext json data. Please see example below.

Public key used to generate JWE can be found [here](https://wiki.verestro.com/display/LV/Configuration). //TODO: add file with public key for dev.

### JWE Examples

During development you can use test methods that allows to generate and check your implementation of JWE:

PAYLOAD - json string used to generate JWE, for example:

<div class="confluence-information-macro confluence-information-macro-information conf-macro output-block" data-hasbody="true" data-macro-name="info" id="bkmrk-%7B%22pan%22%3A%22555544443333"><div class="confluence-information-macro confluence-information-macro-information conf-macro output-block" data-hasbody="true" data-macro-name="info"><div class="confluence-information-macro-body"><div class="code panel pdl conf-macro output-block" data-hasbody="true" data-macro-name="code"><div class="codeContent panelContent pdl"><div><div class="syntaxhighlighter sh-confluence nogutter  java" id="bkmrk-%7B%22pan%22%3A%22555544443333-0"><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="code"><div class="container" title="Hint: double-click to select code"><div class="line number1 index0 alt2">`{``"pan"``:``"5555444433331234"``,``"expiryDate"``:``"2040-11-30"``}`</div></div></td></tr></tbody></table>

</div></div></div></div></div></div></div>ISSUER - string provided by DC Team during integration  
EXAMPLE\_JWE - JWE to be validated and decoded

<div class="code panel pdl conf-macro output-block" data-hasbody="true" data-macro-name="code" id="bkmrk-generate-jwe-curl---"><div class="codeHeader panelHeader pdl">**Generate JWE**</div><div class="codeContent panelContent pdl"><div><div class="syntaxhighlighter sh-confluence nogutter  java" id="bkmrk-curl---location---re"><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="code"><div class="container" title="Hint: double-click to select code"><div class="line number1 index0 alt2">`curl --location --request GET ``'<a href="https://datacore.upaidtest.pl/test/generate-jwe-token/PAYLOAD'">https://datacore.upaidtest.pl/test/generate-jwe-token/PAYLOAD'</a>` `\`</div><div class="line number2 index1 alt1">`--header ``'Content-Type: application/json'` `\`</div><div class="line number3 index2 alt2">`--header ``'Accept: application/json'` `\`</div><div class="line number4 index3 alt1">`--header ``'Authorization: Basic dGVzdDE6dGVzdDEyMw=='` `\`</div><div class="line number5 index4 alt2">`--header ``'ISSUER-CODE: ISSUER'` `\`</div><div class="line number6 index5 alt1">`--header ``'COLLECTION: internal'`</div></div></td></tr></tbody></table>

</div></div></div></div>**Read JWE**

<div class="code panel pdl conf-macro output-block" data-hasbody="true" data-macro-name="code" id="bkmrk-read-jwe-curl---loca"><div class="codeContent panelContent pdl"><div><div class="syntaxhighlighter sh-confluence nogutter  java" id="bkmrk-curl---location---re-0"><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="code"><div class="container" title="Hint: double-click to select code"><div class="line number1 index0 alt2">`curl --location --request POST ``'<a href="https://datacore.upaidtest.pl/test/read-jwe-token'">https://datacore.upaidtest.pl/test/read-jwe-token'</a>` `\`</div><div class="line number2 index1 alt1">`--header ``'Content-Type: application/json'` `\`</div><div class="line number3 index2 alt2">`--header ``'Accept: application/json'` `\`</div><div class="line number4 index3 alt1">`--header ``'Authorization: Basic dGVzdDE6dGVzdDEyMw=='` `\`</div><div class="line number5 index4 alt2">`--header ``'ISSUER-CODE: ISSUER'` `\`</div><div class="line number6 index5 alt1">`--header ``'COLLECTION: internal'` `\`</div><div class="line number7 index6 alt2">`--data-raw '{`</div><div class="line number8 index7 alt1">`    ``"token"` `: ``"EXAMPLE_JWE"`</div><div class="line number9 index8 alt2">`}'`</div></div></td></tr></tbody></table>

</div></div></div></div>**Hash HMAC**

Lifecycle also provides configuration called idType. It is simple tool that can be used if you can't store LC internal id's.  
There are two main id types: useridType and cardIdType. All possible options for both are presented in LC API specification.  
Here we are describing case when cardIdType is set to hash.  
Hash value is as SHA-256 HMAC, please see links below for more details:   
[RFC](https://tools.ietf.org/html/rfc4868#page-3),  
[Wiki](https://en.wikipedia.org/wiki/HMAC),  
[Java example](https://gist.github.com/MaximeFrancoeur/bcb7fc2db08c704f322a).

Test value of key used to calculate HMAC in HEX

59c6d62dde38d8a2c32105a53336b8ef

To validate your implementation please check plain and hashed values below:

<div class="code panel pdl conf-macro output-block" data-hasbody="true" data-macro-name="code" id="bkmrk-%225555444433332222%22%C2%A0%C2%A0"><div class="codeContent panelContent pdl"><div class="syntaxhighlighter sh-confluence nogutter  java" id="bkmrk-%225555444433332222%22%C2%A0%C2%A0-0"><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="code"><div class="container" title="Hint: double-click to select code"><div class="line number1 index0 alt2">`"5555444433332222"` `"4f64c445c859f7e53209e0091a5faef7e8b3ebbad899fbf8c74df09a6bfe5646"`</div><div class="line number2 index1 alt1">`"6984576897634895763948576"` `"4b2eab65ab16183fa6ac8a8b12ad690890db98c5ce20e6d56aa037b723bbe842"`</div><div class="line number3 index2 alt2">`"someTestValue398048096859607"` `"9596a78a7382e90159d8ec78a8d37baff57d05f676c0607dd7fb24b0396270ce"`</div></div></td></tr></tbody></table>

</div></div></div><div class="syntaxhighlighter sh-confluence nogutter  java" id="bkmrk--4"></div><div class="syntaxhighlighter sh-confluence nogutter  java" id="bkmrk--5"></div>
## Lifecycle Import

### Info

Lifecycle file import is a mechanism that allows you to create a file with multiple instructions for api. Each instruction is like an request. Instead of sending 10.000 requests to API, which may take ages, you can create single file with all those instructions and let our system handle it asynchronously. Once import is completed an report is generated which will contain errors and information about processed rows. Files with instructions should be uploaded onto SFTP. To get access details, a new integration needs to be set up for bank.

### Requirements

File storage with directories:

/**input** for input files  
/**output** for output files (reports)  
/**processed** for files that were processed

### Input

Input files should be uploaded onto storage into 'input' directory. Everyday at 12:07 am a cron job will run and process those files.

### File example

File format is json line (jsonl). In short this is a file that contains valid json in every line (separated by line break, not a comma etc.).

Each line is a single instruction for our import mechanism. Example file below:

```
{"method":"addUser","data":{"externalId":"48111111111","firstName":"First","lastName":"User","phone":"48111111111","email":"you@post.com","birthDate":"1979-10-06","wPIN":"1234","state":"VERIFIED"}}
{"method":"addUser","data":{"externalId":"48222222222","firstName":"Second","lastName":"User","phone":"48222222222","email":"me@post.com","birthDate":"1979-10-06","wPIN":"1234","state":"VERIFIED"}}
{"method":"addUser","data":{"externalId":"48333333333","firstName":"Third","lastName":"User","phone":"48333333333","email":"validemail@post.com","birthDate":"1979-10-06","wPIN":"1234","state":"VERIFIED"}}
```

<div class="code panel pdl conf-macro output-block" data-hasbody="true" data-macro-name="code" id="bkmrk--6"><div class="codeContent panelContent pdl"><div><div class="syntaxhighlighter sh-midnight nogutter  js" id="bkmrk--11"></div></div></div></div>### Line explaination

Each line is a JSON with two keys:

**method** - name of method you're calling (supported methods below),  
**data** - request body.

For example result of importing first line of example file is similar to making a request to **/lifecycle/v1/users** with body:

```
{"externalId":"48111111111","firstName":"First","lastName":"User","phone":"48111111111","email":"you@post.com","birthDate":"1979-10-06","wPIN":"1234","state":"VERIFIED"}
```

### Supported methods

At the moment the only one supported method is **addUser** ([https://datacore.upaidtest.pl/documentation-lifecycle/#api-Lifecycle-user\_add](https://datacore.upaidtest.pl/documentation-lifecycle/#api-Lifecycle-user_add)).

### Processing

Processing is handled asynchronously. It means that once file is read, every line will be changed into a "job" and then processed by our "workers". After reading whole file it is moved to **processed** directory. PS. This does not mean that the import is completed.

If workers encounter any troubles during handling their job an error message will be inserted into report file. Structure of error line is "**X, ERROR\_MESSAGE**" where ***X*** is the line number from import file and **ERROR\_MESSAGE** is just the error message. Examples presented in Output section.

### Output

Once import is completed an line with rows processed information is inserted into the report file. You can find report files inside **output** directory. Report file name is $input\_file\_name\_without\_extension-report.csv. Example input/output file with names below.

Example input file

<div class="code panel pdl conf-macro output-block" data-hasbody="true" data-macro-name="code" id="bkmrk-input_file.jsonl"><div class="codeHeader panelHeader pdl">**input\_file.jsonl**</div><div class="codeContent panelContent pdl"><div><div class="syntaxhighlighter sh-midnight  js" id="bkmrk--12"></div></div></div></div>```
{invalid json :((}
{"method":"addUser","data":{"externalId":"48222222222","firstName":"Second","lastName":"User","phone":"48222222222","email":"me@post.com","birthDate":"1979-10-06","wPIN":"1234","state":"VERIFIED"}}
{"method":"addUser","data":{"externalId":"48333333333","firstName":"Third","lastName":"User","phone":"48333333333","email":"validemail@post.com","birthDate":"1337","wPIN":"1234","state":"VERIFIED"}}
{"method":"thisWillNotWork","data":{"externalId":"4833"}}
```

Example output file

<div class="code panel pdl conf-macro output-block" data-hasbody="true" data-macro-name="code" id="bkmrk-input_file-report.cs"><div class="codeHeader panelHeader pdl">**input\_file-report.csv**</div><div class="codeContent panelContent pdl"><div><div class="syntaxhighlighter sh-midnight nogutter  java" id="bkmrk--13"></div></div></div></div>```
1,INVALID_JSON
3,{"errors":{"phone":["VALUE_HAS_TO_BE_UNIQUE"],"birthDate":["DATE_IS_INVALID","DATE_FORMAT_IS_INVALID"]}}
4,INVALID_METHOD
 
Total rows processed: 4
```

Typical error messages:

**INVALID\_JSON** - there was an error while trying to decode line (first line of example input file).  
**INVALID\_METHOD -** invalid/unsupported method (fourth line of example input file).  
**UNKNOWN\_ERROR** - unhandled error (contact DC team for more info).

As you may have noticed there is also an error message encoded in json format. This is the same response that you would receive in a normal api call.

PS. Successfully processed line doesn't produce any output in report file (that's why there is no status for 2nd line).

## Lifecycle API usage

### URL construction

<div data-angle="0" data-canvas-width="103.99666666666664" data-font-name="Helvetica" id="bkmrk-%2Fusers%2F%7Bid%7D%2Fcards%2F%7Bc">/users/{id}/cards/{cardId}?userIdType={userIdType}&amp;cardId=internalId</div><div data-angle="0" data-canvas-width="24.453333333333337" data-font-name="Helvetica" id="bkmrk-id---value-defines-u">  
id - value defines user identifier.</div><div data-angle="0" data-canvas-width="116.73666666666666" data-font-name="Helvetica" id="bkmrk-useridtype---type-of">userIdType - type of value that identifies user. Supported options:</div>- phone,
- email,
- externalId - provided by partner,
- internalId - provided by Verestro (default option),
- hash – card hash provided by bank.

### Headers

<table border="1" class="t1" id="bkmrk-field%C2%A0-description%C2%A0-" style="width: 100%;"><tbody><tr><td class="td1" style="width: 18.905828%;" valign="top">**Field**

</td><td class="td1" style="width: 81.094172%;" valign="top">**Description**

</td></tr><tr><td class="td1" style="width: 18.905828%;" valign="top">Authorization

</td><td class="td1" style="width: 81.094172%;" valign="top">Basic Access Authentication token.

</td></tr><tr><td class="td1" style="width: 18.905828%;" valign="top">Accept

</td><td class="td1" style="width: 81.094172%;" valign="top">application/json<span class="Apple-converted-space"> .</span>

</td></tr><tr><td class="td1" style="width: 18.905828%;" valign="top">Content-Type

</td><td class="td1" style="width: 81.094172%;" valign="top">application/json<span class="Apple-converted-space"> .</span>

</td></tr><tr><td class="td1" style="width: 18.905828%;" valign="top">Accept-Language

</td><td class="td1" style="width: 81.094172%;" valign="top">Options: en, pl, ...<span class="Apple-converted-space">   
</span>

</td></tr><tr><td class="td1" style="width: 18.905828%;" valign="top">Issuer-Code

</td><td class="td1" style="width: 81.094172%;" valign="top">Wallet name. This value is provided by datacore team.

</td></tr><tr><td class="td1" style="width: 18.905828%;" valign="top">Collection

</td><td class="td1" style="width: 81.094172%;" valign="top">Collection name. Options: lifecycle<span class="Apple-converted-space"> .</span>

</td></tr><tr><td class="td1" style="width: 18.905828%;" valign="top">Application-Id

</td><td class="td1" style="width: 81.094172%;" valign="top">The unique name of the application, e.g. "admin-panel"<span class="Apple-converted-space"> .</span>

</td></tr><tr><td class="td1" style="width: 18.905828%;" valign="top">X-B3-SpanId

</td><td class="td1" style="width: 81.094172%;" valign="top">This is the id of the specific operation. Link<span class="Apple-converted-space"> .</span>

</td></tr><tr><td class="td1" style="width: 18.905828%;" valign="top">X-B3-TraceId

</td><td class="td1" style="width: 81.094172%;" valign="top">This is a global id that is used to identify a sequence of requests / responses in the whole system. This means that it should be passed between all websites in requests / responsives. In case it is null then of course you have to generate a new one, because it means that this is the first request from an external client.

</td></tr></tbody></table>