Payment API Client
## Overview
---
This API is a gateway for receiving payments through our system. It's ment to simplify external integration between different domains such as credit cards, prepaid cards and value codes.
## Swagger documentation
---
* [VasPublicPaymentApi](https://stage-evc.payex.com/payment-api/swagger-ui.html)
# Public Payment API Client
---
## Prerequisites
---
* Java 11
* VueJS
* Maven
* Postgres
## Project setup
---
vas-payment-api-client
├─┬ backend → backend module with Spring Boot code
│ ├── src
│ └── pom.xml
├─┬ frontend → frontend module with Vue.js code
│ ├── src
│ └── pom.xml
└── pom.xml → Maven parent pom managing both modules
## Security
---
<details>
<summary>Oauth2:</summary>
VasPublicPaymentApi requires an OAuth2 access token for interaction.
This application automatically handles token fetching and refreshing by using [Spring Security](https://docs.spring.io/spring-security-oauth2-boot/docs/current/reference/htmlsingle/#boot-features-security-custom-user-info-client).
Configuration values are set in [application.yml](https://github.com/PayEx/vas-payment-api-client/blob/master/backend/src/main/resources/application.yml):
# CLIENT_ID/CLIENT_SECRET/VAS_AUTH_SERVER_URL can also be set in docker-compose.yml as environment variables if running with docker
# The application will see if environment variables are present, if not fall back to "XXX" values.
vas-payment-api:
oauth2:
client:
grantType: client_credentials
clientId: "${CLIENT_ID}:XXX"
clientSecret: "${CLIENT_SECRET}:XXX"
accessTokenUri: "${VAS_AUTH_SERVER_URL}:XXX"
scope: publicapi
And the implementation of these are located in [Oauth2RestTemplateConfiguration.java](https://github.com/PayEx/vas-payment-api-client/blob/master/backend/src/main/java/com/payex/vas/demo/config/security/Oauth2RestTemplateConfiguration.java):
//...
@Bean
@ConfigurationProperties("vas-payment-api.oauth2.client")
protected ClientCredentialsResourceDetails oAuthDetails() {
return new ClientCredentialsResourceDetails();
}`
` @Bean
protected RestTemplate restTemplate() {
var restTemplate = new OAuth2RestTemplate(oAuthDetails());
restTemplate.setInterceptors(ImmutableList.of(externalRequestInterceptor()));
restTemplate.setRequestFactory(httpRequestFactory());
return restTemplate;
}
//...
}
```
</details>`
`<details>`
`<summary>HMAC:</summary>`
`The API also requires HMAC authentication to be present in a request.
In this client the HMAC value is automatically calculated by [HmacSignatureBuilder.java](https://github.com/PayEx/vas-payment-api-client/blob/master/backend/src/main/java/com/payex/vas/demo/config/security/HmacSignatureBuilder.java) and added to all outgoing requests in [ExternalRequestInterceptor.java](https://github.com/PayEx/vas-payment-api-client/blob/master/backend/src/main/java/com/payex/vas/demo/config/ExternalRequestInterceptor.java)`
`HMAC is implemented using SHA-512 secure hash algorithm. `
`Expected `Hmac` header format is:`
HmacSHA512
````
wheredigestis a Base64 formatted HMAC SHA512 digest of the following string:
RESOURCE\n
USER\
NONCE\n
DATE\n
PAYLOAD\n
`METHOD (mandatory) the requested method (in upper case) RESOURCE (mandatory) the path to desired resource (without hostname and any query parameters)
NONSE (mandatory) a unique value for each request (UUID) DATE(optional) same as Transmission-Time if provided as seperate header. Uses ISO8601 standard PAYLOAD (optional) body of request `
Example request:
https://stage-evc.payex.com/payment-api/api/payments/payment-account/balance \
-H 'Accept: */*' \
-H 'Agreement-Merchant-Id: XXX' \
-H 'Authorization: Bearer XXX' \
-H 'Hmac: HmacSHA512 user:21a0213e-30eb-85ab-b355-a310d31af30e:oY5Q5Rf1anCz7DRm3GyWR0dvJDnhl/psylfnNCn6FA0NOrQS3L0fvyUsQ1IQ9gQPeLUt9J3IM2zwoSfZpDgRJA==' \
-H 'Transmission-Time: 2019-06-18T09:19:15.208257Z' \
-H 'Session-Id: e0447bd2-ab64-b456-b17b-da274bb8428e' \
-d '{
"accountIdentifier": {
"accountKey": "7013369000000000000",
"cvc": "123",
"expiryDate": "2019-12-31",
"instrument": "GC"
}
}'
In this exampleUSERis user andSECRETis secret.
The plain string todigestwould then be:
/payment-api/api/payments/payment-account/balance
user
21a0213e-30eb-85ab-b355-a310d31af30e
2019-06-18T09:19:15.208257Z
{
"accountIdentifier": {
"accountKey": "7013360000000000000",
"cvc": "123",
"expiryDate": "2020-12-31",
"instrument": "CC"
}
}
The plaindigeststring is then hashed withHmacSHA512algorithm and theSECRET. Finally we Base64 encode the hashed value. This is the finaldigestto be provided in theHmacheader.
FinalHmacheader value:
#### Postman example script
In pre-request script copy/paste the following snippet:
`var user = 'user';
var secret = 'secret';
var transmissionTime = (new Date()).toISOString();
var sessionId = guid();`
`var hmac = generateHMAC(user, secret, transmissionTime);
console.log('hmac: ' + hmac);`
`//Set header values
pm.request.headers.add({key: 'Hmac', value: hmac });
pm.request.headers.add({key: 'Transmission-Time', value: transmissionTime });
pm.request.headers.add({key: 'Session-Id', value: sessionId });`
`function generateHMAC(user, secret, transmissionTime) {`
` var algorithm = "HmacSHA512";
var separator = ":";
var method = request.method.toUpperCase();
var nonce = generateNonce(); //UUID
var date = transmissionTime;
var uri_path = replaceRequestEnv(request.url.trim()).trim().replace(new RegExp('^https?://[^/]+/'), '/'); // strip hostname
uri_path = uri_path.split("?")[0]; //Remove query paramters
var payload = _.isEmpty(request.data) ? "" : request.data;
var macData = method + '\n'
+ uri_path + '\n'
+ user + '\n'
+ nonce + '\n'
+ date + '\n'
+ payload + '\n';`
` macData = replaceRequestEnv(macData);
console.log('data to mac: ' + macData);`
` var hash = CryptoJS.HmacSHA512(macData, secret);
var digest = CryptoJS.enc.Base64.stringify(hash);
return algorithm + " " + user + separator + nonce + separator + digest;
}`
`function replaceRequestEnv(input) { //manually set environments to they are populated before hashing
return input.replace(/{{([^)]+)}}/g, function (str, key) {
var value = pm.environment.get(key);
return value === null ? pm.varables.get(key) : value;
});
}`
`function generateNonce() {
return guid();
}`
`function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}`
` return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}`
`
### Security documentation
---
* [OAuth2](https://oauth.net/2/)
* [Client Credentials](https://www.oauth.com/oauth2-servers/access-tokens/client-credentials/)
* [The RESTful CookBook: HMAC](http://restcookbook.com/Basics/loggingin/)
* [HMAC - Wikipedia](https://en.wikipedia.org/wiki/HMAC)
## First App run
---
**NB! The application expects a PostgreSQL server to be running on localhost with a usernametestand passwordtestto exist.**
**This can automatically be configured if PostgreSQL server is started in docker with environment variablesPOSTGRES_USER=testandPOSTGRES_PASSWORD=testare set (See [docker-compose.yml](https://github.com/PayEx/vas-payment-api-client/blob/master/docker-compose.yml)).**
Inside the root directory, do a:
Run the Spring Boot App:
Now go to <http://localhost:8080/> and have a look at your new client.
## Testing application
---
1. Add a new card with provided details from PayEx.
1. Click on newly added Card
1. Click on "initiate payment" to create a new transaction
## Build docker image:
---
## Deploy to local docker:
---