300 lines
7.2 KiB
Markdown
300 lines
7.2 KiB
Markdown
|
# Revolur Card API
|
||
|
## Base address
|
||
|
https://app.revolut.com/api/revolut-secure/retail
|
||
|
## Headers
|
||
|
```
|
||
|
{
|
||
|
"authority": "app.revolut.com",
|
||
|
"accept": "application/json",
|
||
|
"content-type": "application/jso",
|
||
|
"origin": "chrome-extension://hdlehfdjcalidklijenibmpcdgjfmafn",
|
||
|
"sec-fetch-dest": "empty",
|
||
|
"sec-fetch-mode": "cors",
|
||
|
"sec-fetch-site": "cross-site",
|
||
|
"user-agent": "<chrome user agent>",
|
||
|
"x-browser-application": "BROWSER_EXTENSION",
|
||
|
"x-client-version": "100.0",
|
||
|
"x-device-id": "<random uid>",
|
||
|
"x-device-model": "<same as user agent>"
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Authentication
|
||
|
### POST /signin
|
||
|
#### Description
|
||
|
Login with phone number and passcode and get a token.
|
||
|
#### Request
|
||
|
```
|
||
|
{
|
||
|
"phone":"<+XX phone>",
|
||
|
"password":"<passcode>",
|
||
|
"channel":"APP"
|
||
|
}
|
||
|
```
|
||
|
#### Response
|
||
|
```
|
||
|
{
|
||
|
"tokenId":"<token uid>"
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### POST /token (issue)
|
||
|
#### Description
|
||
|
Once a token is obtained through the login request, the signin request has to be confirmed on the app. This request is used for polling for that authorization and then getting actual credentials.
|
||
|
|
||
|
#### Request
|
||
|
```
|
||
|
{
|
||
|
"phone":"<+XX phone>",
|
||
|
"password":"<passcode>",
|
||
|
"tokenId":"<token uid>"
|
||
|
}
|
||
|
```
|
||
|
|
||
|
#### Response
|
||
|
##### Case 1 - Authorization Pending
|
||
|
```
|
||
|
{
|
||
|
"message": "One should obtain consent from the user before continuing",
|
||
|
"code": 9035
|
||
|
}
|
||
|
```
|
||
|
##### Case 2 - Authorization Granted
|
||
|
```
|
||
|
{
|
||
|
"tokenExpiryDate": <expiry timestamp>,
|
||
|
"refreshCode": "<refresh code>",
|
||
|
"ownerId": "<owner id>,
|
||
|
"accessToken": "<access token>",
|
||
|
"user": {
|
||
|
"id": "<user id> (should be same as <owner id>)",
|
||
|
"state": "ACTIVE"
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
### POST /token (refresh)
|
||
|
#### Description
|
||
|
The token has to be periodically refreshed.
|
||
|
|
||
|
#### Request
|
||
|
```
|
||
|
{
|
||
|
"userId":"<user id>"
|
||
|
"refreshCode":"<refresh code>"
|
||
|
}
|
||
|
```
|
||
|
#### Response
|
||
|
```
|
||
|
{
|
||
|
"tokenExpiryDate": <expiry date>,
|
||
|
"refreshCode": "<refresh code>",
|
||
|
"ownerId": "<owner id>",
|
||
|
"accessToken": "<access token>"
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### GET /user/current/picture
|
||
|
#### Description
|
||
|
Get user profile picture.
|
||
|
|
||
|
#### Headers
|
||
|
```
|
||
|
{
|
||
|
"Authorization": <owner id>:<access token>
|
||
|
}
|
||
|
```
|
||
|
|
||
|
#### Response
|
||
|
Profile picture raw bytes.
|
||
|
|
||
|
### GET /user/current
|
||
|
#### Description
|
||
|
Get user details, including email, phone, full address, username, id, code.
|
||
|
|
||
|
#### Headers
|
||
|
```
|
||
|
{
|
||
|
"Authorization": <owner id>:<access token>
|
||
|
}
|
||
|
```
|
||
|
|
||
|
|
||
|
### Response
|
||
|
```
|
||
|
{
|
||
|
"user": {
|
||
|
"id": "<user id>",
|
||
|
"individualId": "<user id>",
|
||
|
"createdDate": <creation date>,
|
||
|
"address": {
|
||
|
"city": "<city>",
|
||
|
"country": "<country code>",
|
||
|
"postcode": "<post code>",
|
||
|
"region": "<region>",
|
||
|
"streetLine1": "<address line 1>",
|
||
|
"streetLine2": "<address line 2>"
|
||
|
},
|
||
|
"birthDate": [
|
||
|
<year>,
|
||
|
<month>,
|
||
|
<day>
|
||
|
],
|
||
|
"firstName": "<first name>",
|
||
|
"lastName": "<last name>",
|
||
|
"phone": "<+XX phone>",
|
||
|
"email": "<email>",
|
||
|
"emailVerified": true,
|
||
|
"state": "ACTIVE",
|
||
|
"referralCode": "<code>",
|
||
|
"code": "<code>",
|
||
|
"kyc": "PASSED",
|
||
|
"underReview": false,
|
||
|
"locale": "en-GB",
|
||
|
"riskAssessed": false,
|
||
|
"username": "<username>",
|
||
|
"identityDetails": {
|
||
|
"accountPurpose": "DAILY_SPENDING",
|
||
|
"taxResidencies": [],
|
||
|
"identificationNumbers": [
|
||
|
{
|
||
|
"country": "<country code>",
|
||
|
"name": "genericTin",
|
||
|
"value": "<?>"
|
||
|
}
|
||
|
]
|
||
|
},
|
||
|
"hasProfilePicture": true,
|
||
|
"appMode": "FULL"
|
||
|
},
|
||
|
"wallet": {
|
||
|
"baseCurrency": "EUR"
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### GET /my-card/all
|
||
|
#### Description
|
||
|
Get an array of all the available cards in the account, without secret details. In a personal account, cards can be either virtual or physical. Virtual cards can also be tagged as single use (disposable). It is also known whether a card is for professional use (`PRO`) or for personal use (`RETAIL`).
|
||
|
|
||
|
#### Headers
|
||
|
```
|
||
|
{
|
||
|
"Authorization": <owner id>:<access token>
|
||
|
}
|
||
|
```
|
||
|
#### Response
|
||
|
```
|
||
|
[
|
||
|
{
|
||
|
"id": "<card id>",
|
||
|
"walletId": "<wallet id>",
|
||
|
"label": "<label>",
|
||
|
"state": "ACTIVE",
|
||
|
"virtual": true,
|
||
|
"disposable": false,
|
||
|
"credit": false,
|
||
|
"instalment": false,
|
||
|
"customised": false,
|
||
|
"brand": "MASTERCARD",
|
||
|
"design": "<design enum>",
|
||
|
"designGroup": "VIRTUAL",
|
||
|
"colour": "#<hex color>",
|
||
|
"lastUsedDate": <last used>,
|
||
|
"lastFour": "<last four digits>",
|
||
|
"expiryDate": <expiry date>,
|
||
|
"replaced": false,
|
||
|
"createdDate": <created date>,
|
||
|
"productType": "PRO",
|
||
|
"settings": {
|
||
|
"locationSecurityEnabled": false,
|
||
|
"magstripeDisabled": true,
|
||
|
"atmWithdrawalsDisabled": true,
|
||
|
"ecommerceDisabled": false,
|
||
|
"contactlessDisabled": false,
|
||
|
"initial": false,
|
||
|
"pockets": [
|
||
|
{
|
||
|
"id": "<pocket id>",
|
||
|
"pocketType": "MERCHANT"
|
||
|
},
|
||
|
{
|
||
|
"id": "<pocket id>",
|
||
|
"pocketType": "MERCHANT"
|
||
|
}
|
||
|
]
|
||
|
},
|
||
|
"delivery": {
|
||
|
"status": "NONE"
|
||
|
},
|
||
|
"usage": <?>,
|
||
|
"preexpired": false,
|
||
|
"applePayEligible": true,
|
||
|
"googlePayEligible": true,
|
||
|
"cardClickActivationEligible": false,
|
||
|
"panActivationEligible": false
|
||
|
},
|
||
|
{
|
||
|
"id": "<card id>",
|
||
|
"walletId": "<wallet id>",
|
||
|
"label": "<label>",
|
||
|
"state": "ACTIVE",
|
||
|
"virtual": true,
|
||
|
"disposable": false,
|
||
|
"credit": false,
|
||
|
"instalment": false,
|
||
|
"customised": false,
|
||
|
"brand": "VISA",
|
||
|
"design": "<design enum>",
|
||
|
"designGroup": "VIRTUAL",
|
||
|
"colour": "#<hex color>",
|
||
|
"lastUsedDate": <last used>,
|
||
|
"lastFour": <last four digits>",
|
||
|
"expiryDate": "<expiry date>",
|
||
|
"replaced": false,
|
||
|
"createdDate": <created date>,
|
||
|
"productType": "RETAIL",
|
||
|
"settings": {
|
||
|
"locationSecurityEnabled": false,
|
||
|
"magstripeDisabled": true,
|
||
|
"atmWithdrawalsDisabled": true,
|
||
|
"ecommerceDisabled": false,
|
||
|
"contactlessDisabled": false,
|
||
|
"initial": false,
|
||
|
"pockets": []
|
||
|
},
|
||
|
"delivery": {
|
||
|
"status": "NONE"
|
||
|
},
|
||
|
"usage": <?>,
|
||
|
"preexpired": false,
|
||
|
"applePayEligible": true,
|
||
|
"googlePayEligible": true,
|
||
|
"cardClickActivationEligible": false,
|
||
|
"panActivationEligible": false
|
||
|
}
|
||
|
]
|
||
|
```
|
||
|
|
||
|
|
||
|
### GET /my-card/<card uid>/details
|
||
|
#### Description
|
||
|
Get card secret details: pan, cvv, expiration date. The pan and the cvv are encrypted using AES ECB with the first 32 ascii chars of the token as key.
|
||
|
#### Headers
|
||
|
```
|
||
|
{
|
||
|
"Authorization": <owner id>:<access token>
|
||
|
}
|
||
|
```
|
||
|
|
||
|
#### Response
|
||
|
```
|
||
|
{
|
||
|
"pan":"<base64 of AES encrypted pan>",
|
||
|
"cvv":"<base64 of AES encrypted cvv>",
|
||
|
"expiry":{"month":<month>,"year":<year>}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## TODO
|
||
|
Support for card termination, issue, freeze, unfreeze, labeling, and creation is quite simple too.
|