From version 2.1
edited by Thomas Warren
on 2020/01/23 13:25
To version 3.1
edited by Thomas Warren
on 2020/01/23 13:25
Change comment: There is no comment for this version

Summary

Details

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

Tips

If you're starting with XWiki, check out the Getting Started Guide.

Need help?

If you need help with XWiki you can contact: