# 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 download from method [GET /lifecycle/v1/public-key](https://developer.verestro.com/books/user-lifecycle-card-management-api-sdk/page/technical-documentation)

### 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 bis_size="{"x":150,"y":1528,"w":840,"h":30,"abs_x":500,"abs_y":1741}" 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 bis_size="{"x":150,"y":1528,"w":840,"h":30,"abs_x":500,"abs_y":1741}" class="confluence-information-macro confluence-information-macro-information conf-macro output-block" data-hasbody="true" data-macro-name="info"><div bis_size="{"x":150,"y":1528,"w":840,"h":30,"abs_x":500,"abs_y":1741}" class="confluence-information-macro-body"><div bis_size="{"x":150,"y":1528,"w":840,"h":30,"abs_x":500,"abs_y":1741}" class="code panel pdl conf-macro output-block" data-hasbody="true" data-macro-name="code"><div bis_size="{"x":150,"y":1528,"w":840,"h":30,"abs_x":500,"abs_y":1741}" class="codeContent panelContent pdl"><div bis_size="{"x":150,"y":1528,"w":840,"h":30,"abs_x":500,"abs_y":1741}"><div bis_size="{"x":150,"y":1528,"w":840,"h":30,"abs_x":500,"abs_y":1741}" 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 bis_size="{"x":158,"y":1535,"w":465,"h":16,"abs_x":508,"abs_y":1748}" class="container" title="Hint: double-click to select code"><div bis_size="{"x":174,"y":1535,"w":433,"h":16,"abs_x":524,"abs_y":1748}" 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 bis_size="{"x":150,"y":1641,"w":840,"h":142,"abs_x":500,"abs_y":1854}" class="code panel pdl conf-macro output-block" data-hasbody="true" data-macro-name="code" id="bkmrk-generate-jwe-curl---"><div bis_size="{"x":150,"y":1641,"w":840,"h":22,"abs_x":500,"abs_y":1854}" class="codeHeader panelHeader pdl">**Generate JWE**</div><div bis_size="{"x":150,"y":1668,"w":840,"h":116,"abs_x":500,"abs_y":1881}" class="codeContent panelContent pdl"><div bis_size="{"x":150,"y":1668,"w":840,"h":116,"abs_x":500,"abs_y":1881}"><div bis_size="{"x":150,"y":1668,"w":840,"h":116,"abs_x":500,"abs_y":1881}" class="syntaxhighlighter sh-confluence nogutter  java" id="bkmrk-curl---location---re"><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="code"><div bis_size="{"x":158,"y":1675,"w":721,"h":102,"abs_x":508,"abs_y":1888}" class="container" title="Hint: double-click to select code"><div bis_size="{"x":174,"y":1675,"w":689,"h":18,"abs_x":524,"abs_y":1888}" 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 bis_size="{"x":174,"y":1694,"w":689,"h":16,"abs_x":524,"abs_y":1907}" class="line number2 index1 alt1">`--header ``'Content-Type: application/json'` `\`</div><div bis_size="{"x":174,"y":1710,"w":689,"h":16,"abs_x":524,"abs_y":1923}" class="line number3 index2 alt2">`--header ``'Accept: application/json'` `\`</div><div bis_size="{"x":174,"y":1727,"w":689,"h":16,"abs_x":524,"abs_y":1940}" class="line number4 index3 alt1">`--header ``'Authorization: Basic dGVzdDE6dGVzdDEyMw=='` `\`</div><div bis_size="{"x":174,"y":1744,"w":689,"h":16,"abs_x":524,"abs_y":1957}" class="line number5 index4 alt2">`--header ``'ISSUER-CODE: ISSUER'` `\`</div><div bis_size="{"x":174,"y":1761,"w":689,"h":16,"abs_x":524,"abs_y":1974}" class="line number6 index5 alt1">`--header ``'COLLECTION: internal'`</div></div></td></tr></tbody></table>

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

<div bis_size="{"x":150,"y":1845,"w":840,"h":166,"abs_x":500,"abs_y":2058}" class="code panel pdl conf-macro output-block" data-hasbody="true" data-macro-name="code" id="bkmrk-read-jwe-curl---loca"><div bis_size="{"x":150,"y":1845,"w":840,"h":166,"abs_x":500,"abs_y":2058}" class="codeContent panelContent pdl"><div bis_size="{"x":150,"y":1845,"w":840,"h":166,"abs_x":500,"abs_y":2058}"><div bis_size="{"x":150,"y":1845,"w":840,"h":166,"abs_x":500,"abs_y":2058}" 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 bis_size="{"x":158,"y":1852,"w":643,"h":153,"abs_x":508,"abs_y":2065}" class="container" title="Hint: double-click to select code"><div bis_size="{"x":174,"y":1852,"w":611,"h":18,"abs_x":524,"abs_y":2065}" 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 bis_size="{"x":174,"y":1871,"w":611,"h":16,"abs_x":524,"abs_y":2084}" class="line number2 index1 alt1">`--header ``'Content-Type: application/json'` `\`</div><div bis_size="{"x":174,"y":1887,"w":611,"h":16,"abs_x":524,"abs_y":2100}" class="line number3 index2 alt2">`--header ``'Accept: application/json'` `\`</div><div bis_size="{"x":174,"y":1904,"w":611,"h":16,"abs_x":524,"abs_y":2117}" class="line number4 index3 alt1">`--header ``'Authorization: Basic dGVzdDE6dGVzdDEyMw=='` `\`</div><div bis_size="{"x":174,"y":1921,"w":611,"h":16,"abs_x":524,"abs_y":2134}" class="line number5 index4 alt2">`--header ``'ISSUER-CODE: ISSUER'` `\`</div><div bis_size="{"x":174,"y":1938,"w":611,"h":16,"abs_x":524,"abs_y":2151}" class="line number6 index5 alt1">`--header ``'COLLECTION: internal'` `\`</div><div bis_size="{"x":174,"y":1954,"w":611,"h":16,"abs_x":524,"abs_y":2167}" class="line number7 index6 alt2">`--data-raw '{`</div><div bis_size="{"x":174,"y":1971,"w":611,"h":16,"abs_x":524,"abs_y":2184}" class="line number8 index7 alt1">`    ``"token"` `: ``"EXAMPLE_JWE"`</div><div bis_size="{"x":174,"y":1988,"w":611,"h":16,"abs_x":524,"abs_y":2201}" 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 bis_size="{"x":150,"y":2373,"w":840,"h":63,"abs_x":500,"abs_y":2586}" class="code panel pdl conf-macro output-block" data-hasbody="true" data-macro-name="code" id="bkmrk-%225555444433332222%22%C2%A0%C2%A0"><div bis_size="{"x":150,"y":2373,"w":840,"h":63,"abs_x":500,"abs_y":2586}" class="codeContent panelContent pdl"><div bis_size="{"x":150,"y":2373,"w":840,"h":63,"abs_x":500,"abs_y":2586}" 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 bis_size="{"x":158,"y":2380,"w":727,"h":50,"abs_x":508,"abs_y":2593}" class="container" title="Hint: double-click to select code"><div bis_size="{"x":174,"y":2380,"w":695,"h":16,"abs_x":524,"abs_y":2593}" class="line number1 index0 alt2">`"5555444433332222"` `"4f64c445c859f7e53209e0091a5faef7e8b3ebbad899fbf8c74df09a6bfe5646"`</div><div bis_size="{"x":174,"y":2397,"w":695,"h":16,"abs_x":524,"abs_y":2610}" class="line number2 index1 alt1">`"6984576897634895763948576"` `"4b2eab65ab16183fa6ac8a8b12ad690890db98c5ce20e6d56aa037b723bbe842"`</div><div bis_size="{"x":174,"y":2414,"w":695,"h":16,"abs_x":524,"abs_y":2627}" class="line number3 index2 alt2">`"someTestValue398048096859607"` `"9596a78a7382e90159d8ec78a8d37baff57d05f676c0607dd7fb24b0396270ce"`</div></div></td></tr></tbody></table>

</div></div></div><div bis_size="{"x":150,"y":2456,"w":840,"h":0,"abs_x":500,"abs_y":2669}" class="syntaxhighlighter sh-confluence nogutter  java" id="bkmrk--4"></div><div bis_size="{"x":150,"y":2456,"w":840,"h":0,"abs_x":500,"abs_y":2669}" class="syntaxhighlighter sh-confluence nogutter  java" id="bkmrk--5"></div>
### Trusted Identity use-case with MDC SDK

In the integrated model, Trusted Identity is used to proof User authenticity. User is firstly authenticated on the Customer side. Trusted Identity should be generated on Issuer backend side and pass via Wallet SDK, since mobile environment is treated as unsecure. Algorithm of generating Trusted Identity is placed in Wallet SDK API specification.

Access to User data stored on Wallet Server is possible only when session is established. After paring device session is automatically generated for particular User.

@startuml  
skinparam ParticipantPadding 30  
skinparam BoxPadding 30  
skinparam noteFontColor #FFFFFF  
skinparam noteBackgroundColor #1C1E3F  
skinparam noteBorderColor #1C1E3F  
skinparam noteBorderThickness 1  
skinparam sequence {  
ArrowColor #1C1E3F  
ArrowFontColor #1C1E3F  
ActorBorderColor #1C1E3F  
ActorBackgroundColor #FFFFFF  
ActorFontStyle bold  
ParticipantBorderColor #1C1E3F  
ParticipantBackgroundColor #1C1E3F  
ParticipantFontColor #FFFFFF  
ParticipantFontStyle bold  
LifeLineBackgroundColor #1C1E3F  
LifeLineBorderColor #1C1E3F  
}  
actor User  
User -&gt; MPA: Login by trusted identity  
activate "MPA"  
MPA -&gt; "Mobile DC": loginByTrustedIdentity(trustedIdentity: String)  
activate "Mobile DC"  
break If data is incorrect  
"Mobile DC" --&gt;&gt; "MPA": Login by trusted identity failure with MdcValidationExceptionError  
end  
group Build login request  
"Mobile DC" -&gt; "Mobile DC": Set userId from prefs  
note left  
userId is save after successful pairing  
end note  
break If userId not exist  
"Mobile DC" -&gt; "MPA": Login by trusted identity failure with CoreSdkException  
end  
"Mobile DC" -&gt; "Mobile DC": Set deviceInstallationId from prefs  
note left  
deviceInstallationId is save after successful pairing  
end note  
break If deviceInstallationId not exist  
"Mobile DC" -&gt; "MPA": Login by trusted identity failure with CoreSdkException  
end  
"Mobile DC" -&gt; "Mobile DC": Set trustedIdentity  
end  
"Mobile DC" -&gt; "Mobile DC Service": getMdcsCertificate()  
activate "Mobile DC Service"  
break If getMdcsCertificate is failure  
"Mobile DC Service" --&gt;&gt; "Mobile DC": failure  
"Mobile DC" --&gt;&gt; "MPA": Login by trusted identity failure with BackendException  
end  
"Mobile DC Service" --&gt;&gt; "Mobile DC": GetMdcsServerCertificateResponse(pemPublicKeyCert)  
deactivate "Mobile DC Service"  
"Mobile DC" -&gt; "Mobile DC": Save mdcsCertificate  
"Mobile DC" -&gt; "Mobile DC Service": loginByTrustedIdentity(UserLoginRequestEncrypted)  
activate "Mobile DC Service"  
break Pair request is failure  
"Mobile DC Service" --&gt;&gt; "Mobile DC": failure  
"Mobile DC" --&gt;&gt; "MPA": Login by trusted identity failure with BackendException  
end  
"Mobile DC Service" --&gt;&gt; "Mobile DC": UserLoginResponse(token)  
deactivate "Mobile DC Service"  
"Mobile DC" -&gt; "Mobile DC": Save token as userAuthToken  
"Mobile DC" -&gt; "Mobile DC": Clear sensitive data  
"Mobile DC" -&gt; "MPA": Login by trusted identity complete  
deactivate "Mobile DC"  
"MPA" -&gt; "User": Show login by trusted identity result  
deactivate "MPA"  
@enduml

<span style="color: rgb(28, 30, 63); font-family: -apple-system, 'system-ui', 'Segoe UI', Oxygen, Ubuntu, Roboto, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; font-size: 2.8275em; font-weight: 400;">Lifecycle Import</span>

### 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 bis_size="{"x":150,"y":5081,"w":840,"h":0,"abs_x":500,"abs_y":5294}" class="code panel pdl conf-macro output-block" data-hasbody="true" data-macro-name="code" id="bkmrk--6"><div bis_size="{"x":150,"y":5081,"w":840,"h":0,"abs_x":500,"abs_y":5294}" class="codeContent panelContent pdl"><div bis_size="{"x":150,"y":5081,"w":840,"h":0,"abs_x":500,"abs_y":5294}"><div bis_size="{"x":150,"y":5081,"w":840,"h":0,"abs_x":500,"abs_y":5294}" 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"}
```

### 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 bis_size="{"x":150,"y":5754,"w":840,"h":22,"abs_x":500,"abs_y":5967}" class="code panel pdl conf-macro output-block" data-hasbody="true" data-macro-name="code" id="bkmrk-input_file.jsonl"><div bis_size="{"x":150,"y":5754,"w":840,"h":22,"abs_x":500,"abs_y":5967}" class="codeHeader panelHeader pdl">**input\_file.jsonl**</div><div bis_size="{"x":150,"y":5776,"w":840,"h":0,"abs_x":500,"abs_y":5989}" class="codeContent panelContent pdl"><div bis_size="{"x":150,"y":5776,"w":840,"h":0,"abs_x":500,"abs_y":5989}"><div bis_size="{"x":150,"y":5776,"w":840,"h":0,"abs_x":500,"abs_y":5989}" 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 bis_size="{"x":150,"y":5928,"w":840,"h":22,"abs_x":500,"abs_y":6141}" class="code panel pdl conf-macro output-block" data-hasbody="true" data-macro-name="code" id="bkmrk-input_file-report.cs"><div bis_size="{"x":150,"y":5928,"w":840,"h":22,"abs_x":500,"abs_y":6141}" class="codeHeader panelHeader pdl">**input\_file-report.csv**</div><div bis_size="{"x":150,"y":5950,"w":840,"h":0,"abs_x":500,"abs_y":6163}" class="codeContent panelContent pdl"><div bis_size="{"x":150,"y":5950,"w":840,"h":0,"abs_x":500,"abs_y":6163}"><div bis_size="{"x":150,"y":5950,"w":840,"h":0,"abs_x":500,"abs_y":6163}" 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).