# Wiki source code of Payment API Client

Last modified by Thomas Warren on 2020/01/23 13:42

#### Export

#### Office Formats

Export as PDF Export as ODT Export as RTF

#### Other Formats

Select the pages to export:

select all / none

Export as HTML
Show last authors

1 | ## Prerequisites
2 |
3 | ---
4 |
5 | * Java 11
6 | * VueJS
7 | * Maven
8 | * Postgres
9 |
10 | ## GitHub
11 | ---
12 |
13 | * [Payment Api Client](https://github.com/PayEx/vas-payment-api-client)
14 |
15 | ## Swagger documentation
16 |
17 | ---
18 |
19 | * [Payment API Swagger](https://stage-evc.payex.com/payment-api/swagger-ui.html)
20 |
21 | ## Project setup
22 |
23 | ---
24 |
25 | vas-payment-api-client
26 | ├─┬ backend → backend module with Spring Boot code
27 | │ ├── src
28 | │ └── pom.xml
29 | ├─┬ frontend → frontend module with Vue.js code
30 | │ ├── src
31 | │ └── pom.xml
32 | └── pom.xml → Maven parent pom managing both modules
33 |
34 | ## Security
35 |
36 | ---
37 |
38 | <details>
39 | <summary>Oauth2:</summary>
40 |
41 | VasPublicPaymentApi requires an OAuth2 access token for interaction.
42 | 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).
43 | Configuration values are set in [application.yml](https://github.com/PayEx/vas-payment-api-client/blob/master/backend/src/main/resources/application.yml):
44 |
45 | ```yaml
46 | # "XXX" Should be replaced by value provided by PayEx
47 | # CLIENT_ID/CLIENT_SECRET/VAS_AUTH_SERVER_URL can also be set in docker-compose.yml as environment variables if running with docker
48 | # The application will see if environment variables are present, if not fall back to "XXX" values.
49 | vas-payment-api:
50 | oauth2:
51 | client:
52 | grantType: client_credentials
53 | clientId: "${CLIENT_ID}:XXX"
54 | clientSecret: "${CLIENT_SECRET}:XXX"
55 | accessTokenUri: "${VAS_AUTH_SERVER_URL}:XXX"
56 | scope: publicapi
57 |
58 | ```
59 |
60 | 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):
61 |
62 | ```java
63 | public class Oauth2RestTemplateConfiguration {
64 | //...
65 | @Bean
66 | @ConfigurationProperties("vas-payment-api.oauth2.client")
67 | protected ClientCredentialsResourceDetails oAuthDetails() {
68 | return new ClientCredentialsResourceDetails();
69 | }
70 |
71 | @Bean
72 | protected RestTemplate restTemplate() {
73 | var restTemplate = new OAuth2RestTemplate(oAuthDetails());
74 | restTemplate.setInterceptors(ImmutableList.of(externalRequestInterceptor()));
75 | restTemplate.setRequestFactory(httpRequestFactory());
76 | return restTemplate;
77 | }
78 | //...
79 | }
80 | ```
81 | </details>
82 |
83 | <details>
84 |
85 | <summary>HMAC:</summary>
86 |
87 | The API also requires HMAC authentication to be present in a request.
88 | 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)
89 |
90 | HMAC is implemented using SHA-512 secure hash algorithm.
91 |
92 | Expected `Hmac` header format is:
93 |
94 | ```text
95 | HmacSHA512 <user>:<nonce>:<digest>
96 | ```
97 |
98 | where `digest` is a Base64 formatted HMAC SHA512 digest of the following string:
99 |
100 | ```text
101 | METHOD\n
102 | RESOURCE\n
103 | USER\
104 | NONCE\n
105 | DATE\n
106 | PAYLOAD\n
107 | ```
108 |
109 | `METHOD` (mandatory) the requested method (in upper case) `RESOURCE` (mandatory) the path to desired resource (without hostname and any query parameters)
110 | `NONSE` (mandatory) a unique value for each request ([UUID](https://tools.ietf.org/rfc/rfc4122.txt)) `DATE`(optional) same as `Transmission-Time` if provided as seperate header. Uses [ISO8601 standard](https://en.wikipedia.org/wiki/ISO_8601) `PAYLOAD` (optional) body of request
111 |
112 | Example request:
113 |
114 | ```bash
115 | curl -X POST \
116 | https://stage-evc.payex.com/payment-api/api/payments/payment-account/balance \
117 | -H 'Accept: */*' \
118 | -H 'Agreement-Merchant-Id: XXX' \
119 | -H 'Authorization: Bearer XXX' \
120 | -H 'Hmac: HmacSHA512 user:21a0213e-30eb-85ab-b355-a310d31af30e:oY5Q5Rf1anCz7DRm3GyWR0dvJDnhl/psylfnNCn6FA0NOrQS3L0fvyUsQ1IQ9gQPeLUt9J3IM2zwoSfZpDgRJA==' \
121 | -H 'Transmission-Time: 2019-06-18T09:19:15.208257Z' \
122 | -H 'Session-Id: e0447bd2-ab64-b456-b17b-da274bb8428e' \
123 | -d '{
124 | "accountIdentifier": {
125 | "accountKey": "7013369000000000000",
126 | "cvc": "123",
127 | "expiryDate": "2019-12-31",
128 | "instrument": "GC"
129 | }
130 | }'
131 | ```
132 |
133 | In this example `USER` is user and `SECRET` is secret.
134 |
135 | The plain string to `digest` would then be:
136 |
137 | ```text
138 | POST
139 | /payment-api/api/payments/payment-account/balance
140 | user
141 | 21a0213e-30eb-85ab-b355-a310d31af30e
142 | 2019-06-18T09:19:15.208257Z
143 | {
144 | "accountIdentifier": {
145 | "accountKey": "7013360000000000000",
146 | "cvc": "123",
147 | "expiryDate": "2020-12-31",
148 | "instrument": "CC"
149 | }
150 | }
151 | ```
152 |
153 | The plain `digest` string is then hashed with `HmacSHA512` algorithm and the `SECRET`. Finally we Base64 encode the hashed value. This is the final `digest` to be provided in the `Hmac` header.
154 |
155 | Final `Hmac` header value:
156 |
157 | ```text
158 | HmacSHA512 user:21a0213e-30eb-85ab-b355-a310d31af30e:oY5Q5Rf1anCz7DRm3GyWR0dvJDnhl/psylfnNCn6FA0NOrQS3L0fvyUsQ1IQ9gQPeLUt9J3IM2zwoSfZpDgRJA==
159 | ```
160 |
161 | #### Postman example script
162 |
163 | In pre-request script copy/paste the following snippet:
164 |
165 | ```javascript
166 |
167 | var user = 'user';
168 | var secret = 'secret';
169 | var transmissionTime = (new Date()).toISOString();
170 | var sessionId = guid();
171 |
172 | var hmac = generateHMAC(user, secret, transmissionTime);
173 | console.log('hmac: ' + hmac);
174 |
175 | //Set header values
176 | pm.request.headers.add({key: 'Hmac', value: hmac });
177 | pm.request.headers.add({key: 'Transmission-Time', value: transmissionTime });
178 | pm.request.headers.add({key: 'Session-Id', value: sessionId });
179 |
180 | function generateHMAC(user, secret, transmissionTime) {
181 |
182 | var algorithm = "HmacSHA512";
183 | var separator = ":";
184 | var method = request.method.toUpperCase();
185 | var nonce = generateNonce(); //UUID
186 | var date = transmissionTime;
187 |
188 | var uri_path = replaceRequestEnv(request.url.trim()).trim().replace(new RegExp('^https?://[^/]+/'), '/'); // strip hostname
189 | uri_path = uri_path.split("?")[0]; //Remove query paramters
190 | var payload = _.isEmpty(request.data) ? "" : request.data;
191 | var macData = method + '\n'
192 | + uri_path + '\n'
193 | + user + '\n'
194 | + nonce + '\n'
195 | + date + '\n'
196 | + payload + '\n';
197 |
198 | macData = replaceRequestEnv(macData);
199 | console.log('data to mac: ' + macData);
200 |
201 | var hash = CryptoJS.HmacSHA512(macData, secret);
202 | var digest = CryptoJS.enc.Base64.stringify(hash);
203 | return algorithm + " " + user + separator + nonce + separator + digest;
204 | }
205 |
206 | function replaceRequestEnv(input) { //manually set environments to they are populated before hashing
207 | return input.replace(/{{([^)]+)}}/g, function (str, key) {
208 | var value = pm.environment.get(key);
209 | return value === null ? pm.varables.get(key) : value;
210 | });
211 | }
212 |
213 | function generateNonce() {
214 | return guid();
215 | }
216 |
217 | function guid() {
218 | function s4() {
219 | return Math.floor((1 + Math.random()) * 0x10000)
220 | .toString(16)
221 | .substring(1);
222 | }
223 |
224 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
225 | s4() + '-' + s4() + s4() + s4();
226 | }
227 |
228 | ```
229 | </details>
230 |
231 | ### Security documentation
232 |
233 | ---
234 |
235 | * [OAuth2](https://oauth.net/2/)
236 | * [Client Credentials](https://www.oauth.com/oauth2-servers/access-tokens/client-credentials/)
237 | * [The RESTful CookBook: HMAC](http://restcookbook.com/Basics/loggingin/)
238 | * [HMAC - Wikipedia](https://en.wikipedia.org/wiki/HMAC)
239 |
240 | ## First App run
241 |
242 | ---
243 |
244 | **NB! The application expects a PostgreSQL server to be running on localhost with a username `test` and password `test` to exist.**
245 | **This can automatically be configured if PostgreSQL server is started in docker with environment variables `POSTGRES_USER=test` and `POSTGRES_PASSWORD=test` are set (See [docker-compose.yml](https://github.com/PayEx/vas-payment-api-client/blob/master/docker-compose.yml)).**
246 |
247 | Inside the root directory, do a:
248 |
249 | ```bash
250 | mvn clean install
251 | ```
252 |
253 | Run the Spring Boot App:
254 |
255 | ```bash
256 | mvn --projects backend spring-boot:run
257 | ```
258 |
259 | Now go to <http://localhost:8080/> and have a look at your new client.
260 |
261 | ## Testing application
262 |
263 | ---
264 |
265 | 1. Add a new card with provided details from PayEx.
266 | 1. Click on newly added Card
267 | 1. Click on "initiate payment" to create a new transaction
268 |
269 | ## Build docker image:
270 |
271 | ---
272 |
273 | ```bash
274 | mvn --projects backend clean compile jib:dockerBuild
275 | ```
276 |
277 | ## Deploy to local docker:
278 |
279 | ---
280 |
281 | ```bash
282 | docker-compose up -d
283 | ```

# Applications

# Tips

Jump to any page in the wiki with Ctrl+G or Meta+G.
See more shortcuts.

# Need help?

If you need help with XWiki you can contact:

- Community Support
- Professional Support