Skip to content

Commit dd5e4b1

Browse files
authored
Merge pull request #8 from bprokopc/master
Update Java SpringBoot example
2 parents e922769 + cd8c2ab commit dd5e4b1

21 files changed

Lines changed: 374 additions & 271 deletions

dotnet/asp.net.mvc5/Flex/Services/SecurityService.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ public class SecurityService: ISecurityService
3434
private static string flexKeysEndpoint = "https://testflex.cybersource.com/cybersource/flex/v1/keys";
3535

3636
private string mid;
37-
private string profileId;
3837
private string cmmKey;
3938
private string organizationId;
4039
private string keyStoreFile;
@@ -45,7 +44,6 @@ public SecurityService()
4544
{
4645
// Read in flex configuration from web.config
4746
mid = WebConfigurationManager.AppSettings["mid"];
48-
profileId = WebConfigurationManager.AppSettings["profileId"];
4947
cmmKey = WebConfigurationManager.AppSettings["cmmKey"];
5048
organizationId = WebConfigurationManager.AppSettings["organizationId"];
5149
keyStoreFile = WebConfigurationManager.AppSettings["keyStoreFile"];
@@ -116,7 +114,6 @@ public async Task<KeyResult> FetchKeystore()
116114
// prepare keys endpoint request payload
117115
var payload = new KeyParameters()
118116
{
119-
profileId = profileId,
120117
encryptionType = "WebCryptoAPI"
121118
};
122119

dotnet/asp.net.mvc5/Flex/Web.config

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
<!-- FLEX configuration -->
1515
<add key="mid" value="YOUR_MID" />
16-
<add key="profileId" value="YOUR_PROFILE" />
1716
<add key="cmmKey" value="123456789012345678901234567890" />
1817
<add key="organizationId" value="YOUR_ORD_ID" />
1918
<add key="keyStoreFile" value="C:\your_keystore_file.p12" />

dotnet/asp.net.mvc5/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ A minimalist C# ASP.NET example integration using Flex-API tokenization.
1313
```xml
1414
<appSettings>
1515
<add key="mid" value="YOUR_MID" />
16-
<add key="profileId" value="YOUR_PROFILE" />
1716
<add key="cmmKey" value="123456789012345678901234567890" />
1817
<add key="organizationId" value="YOUR_ORD_ID" />
1918
<add key="keyStoreFile" value="C:\your_keystore_file.p12" />

java/spring-boot/.gitignore

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,42 @@
1-
# Covers all spring-boot ignore files
2-
# Reference: https://github.com/spring-projects/spring-boot/blob/master/.gitignore
3-
4-
.gradle
5-
*.sw?
6-
.#*
7-
*#
8-
*~
9-
/build
10-
/code
11-
.classpath
12-
.project
13-
.settings
14-
.metadata
15-
.factorypath
16-
.recommenders
17-
bin
18-
build
19-
lib/
20-
target
21-
.factorypath
22-
.springBeans
23-
interpolated*.xml
24-
dependency-reduced-pom.xml
25-
build.log
26-
_site/
27-
.*.md.html
28-
manifest.yml
29-
MANIFEST.MF
30-
settings.xml
31-
activemq-data
32-
overridedb.*
33-
*.iml
34-
*.ipr
35-
*.iws
36-
.idea
37-
*.jar
38-
.DS_Store
39-
.factorypath
40-
dump.rdb
1+
# Covers all spring-boot ignore files
2+
# Reference: https://github.com/spring-projects/spring-boot/blob/master/.gitignore
3+
4+
.gradle
5+
*.sw?
6+
.#*
7+
*#
8+
*~
9+
/build
10+
/code
11+
.classpath
12+
.project
13+
.settings
14+
.metadata
15+
.factorypath
16+
.recommenders
17+
bin
18+
build
19+
lib/
20+
target
21+
.factorypath
22+
.springBeans
23+
interpolated*.xml
24+
dependency-reduced-pom.xml
25+
build.log
26+
_site/
27+
.*.md.html
28+
manifest.yml
29+
MANIFEST.MF
30+
settings.xml
31+
activemq-data
32+
overridedb.*
33+
*.iml
34+
*.ipr
35+
*.iws
36+
.idea
37+
*.jar
38+
.DS_Store
39+
.factorypath
40+
dump.rdb
41+
42+
nbactions.xml

java/spring-boot/README.md

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,11 @@ A minimalist java/spring-boot example integration using Flex-API tokenization.
1010

1111
## Setup Instructions
1212

13-
1. Modify `./src/main/resources/application.properties` with the credentials provided by customer support in your private BETA invite and that your `.p12` key is placed in the directory specified.
13+
1. Modify `./src/main/resources/application.properties` with the credentials created through [VISA Developer Portal](https://developer.visa.com/).
1414

1515
```properties
16-
mid=YOUR_MID
17-
profileId=YOUR_PROFILE
18-
cmmKey=123456789012345678901234567890
19-
organizationId=YOUR_ORD_ID
20-
keyStoreFile=/your_keystore_file.p12
21-
keyStorePassword=YOUR_KEYSTORE_PASS
22-
privateKeyPassword=YOUR_PRIVKEY_PASS
16+
vdp.api-key=_YOUR_APPLICATION_SPECIFIC_API_KEY_
17+
vdp.shared-secret=_YOUR_APPLICATION_SPECIFIC_SHARED_SECRET_
2318
```
2419

2520
2. Build and run the application using maven
@@ -30,7 +25,7 @@ A minimalist java/spring-boot example integration using Flex-API tokenization.
3025

3126
## Tips
3227

33-
- If you are having issues, checkout the full [FLEX documentation](http://apps.cybersource.com/library/documentation/dev_guides/Secure_Acceptance_Flex/html/).
28+
- If you are having issues, checkout the full [FLEX documentation](https://developer.visa.com/products/cybersource/reference#cybersource__cybersource_flex_api).
3429

3530
- To change the port the application is served on, update `server.port=8443` in `application.properties`, replacing `8443` with your desired port number.
3631

java/spring-boot/pom.xml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,5 @@
4848
<groupId>org.springframework.boot</groupId>
4949
<artifactId>spring-boot-starter-web</artifactId>
5050
</dependency>
51-
<!-- bouncycastle for cryptography -->
52-
<dependency>
53-
<groupId>org.bouncycastle</groupId>
54-
<artifactId>bcprov-jdk15on</artifactId>
55-
<version>1.51</version>
56-
</dependency>
5751
</dependencies>
5852
</project>
Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
/**
2-
* Copyright (c) 2016 by CyberSource
3-
* Governing licence: https://github.com/CyberSource/cybersource-flex-samples/blob/master/LICENSE.md
4-
*/
5-
2+
* Copyright (c) 2016 by CyberSource
3+
* Governing licence: https://github.com/CyberSource/cybersource-flex-samples/blob/master/LICENSE.md
4+
*/
65
package com.cybersource.flex.application;
76

87
import com.cybersource.flex.models.KeyParameters;
8+
import com.cybersource.flex.models.KeyParametersMessageConverter;
99
import com.cybersource.flex.models.KeyResult;
10+
import com.cybersource.flex.vdp.VDPEnpoints;
1011
import java.security.PublicKey;
1112
import java.util.Map;
13+
import javax.annotation.PostConstruct;
1214
import javax.servlet.http.HttpSession;
1315
import org.springframework.beans.factory.annotation.Autowired;
1416
import org.springframework.beans.factory.annotation.Value;
15-
import org.springframework.http.HttpEntity;
16-
import org.springframework.http.HttpHeaders;
1717
import org.springframework.stereotype.Controller;
1818
import org.springframework.ui.Model;
1919
import org.springframework.web.bind.annotation.RequestMapping;
@@ -24,16 +24,23 @@
2424
@Controller
2525
public class CheckoutController {
2626

27-
@Value("${mid}")
28-
private String mid;
29-
@Value("${profileId}")
30-
private String profileId;
27+
@Value("${vdp.api-key}")
28+
private String apiKey;
29+
@Value("${vdp.shared-secret}")
30+
private String sharedSecret;
3131

32-
private final static String flexKeysEndpoint = "https://testflex.cybersource.com/cybersource/flex/v1/keys";
32+
private final FlexSecurityService flexSecurityService;
33+
private final RestTemplate restTemplate = new RestTemplate();
3334

3435
@Autowired
35-
private SecurityService securityService;
36-
private final RestTemplate restTemplate = new RestTemplate();
36+
public CheckoutController(final FlexSecurityService flexSecurityService) {
37+
this.flexSecurityService = flexSecurityService;
38+
}
39+
40+
@PostConstruct
41+
private void postConstruct() {
42+
restTemplate.getMessageConverters().add(0, new KeyParametersMessageConverter(apiKey, sharedSecret));
43+
}
3744

3845
@RequestMapping("/")
3946
String redirect() {
@@ -42,37 +49,26 @@ String redirect() {
4249

4350
@RequestMapping("/checkout")
4451
String checkout(final HttpSession session, final Model model) {
45-
// prepare HTTP headers to make server2flex rest call
46-
final HttpHeaders headers = new HttpHeaders();
47-
headers.set("X-MERCHANT-ID", mid);
48-
securityService.addSignature(headers);
52+
// retrieve one time use public RSA key from Flex to facilitate PAN encryption
53+
final KeyParameters keyParameters = new KeyParameters();
54+
KeyResult key = restTemplate.postForObject(VDPEnpoints.Sandbox.keysUrl(apiKey), keyParameters, KeyResult.class);
4955

50-
// prepare keys endpoint request payload
51-
KeyParameters requestBody = new KeyParameters();
52-
requestBody.setProfileId(profileId); // merchant profile
53-
requestBody.setEncryptionType("WebCryptoAPI"); // encryption type
54-
55-
// retrieve one time use RSA public key
56-
HttpEntity<KeyParameters> httpEntity = new HttpEntity<>(requestBody, headers);
57-
KeyResult key = restTemplate.postForObject(flexKeysEndpoint, httpEntity, KeyResult.class);
58-
59-
// parse Flex public key in DER format and store it in session for future use.
60-
PublicKey flexPublicKey = securityService.decodePublicKey(key.getDer().getPublicKey());
56+
// parse Flex public key in DER format and store it in session for future use (i.e. to verify token signature)
57+
PublicKey flexPublicKey = flexSecurityService.decodePublicKey(key.getDer().getPublicKey());
6158
session.setAttribute("flexPublicKey", flexPublicKey);
6259

63-
// Add JSON Web Keystore to the view model and return rendered page
60+
// Add JSON Web Keystore to the view model and return rendered "checkout" page
6461
model.addAttribute("jwk", key.getJwk());
6562
return "checkout";
6663
}
6764

6865
@RequestMapping(value = "/receipt", method = RequestMethod.POST)
6966
String receipt(@RequestParam final Map<String, Object> postParams, final HttpSession session, final Model model) {
70-
7167
// Read in the public key to use and remove it from the session
7268
PublicKey flexPublicKey = (PublicKey) session.getAttribute("flexPublicKey");
73-
session.removeAttribute("flexPublicKey");
69+
session.removeAttribute("flexPublicKey"); // no longer needed
7470

75-
// verify Flex signature passed as POST parameters
71+
// verify Flex signature passed as POST parameter
7672
String signedFields = (String) postParams.get("flex_signedFields");
7773
StringBuilder sb = new StringBuilder();
7874
for (String k : signedFields.split(",")) {
@@ -81,14 +77,21 @@ String receipt(@RequestParam final Map<String, Object> postParams, final HttpSes
8177
}
8278
final String signedValues = sb.substring(1);
8379
final String signature = (String) postParams.get("flex_signature");
84-
if (!securityService.verify(flexPublicKey, signedValues, signature)) {
80+
if (!flexSecurityService.verify(flexPublicKey, signedValues, signature)) {
8581
throw new RuntimeException("The signature is not valid");
8682
}
8783

88-
// Add the post params to our view model
89-
model.addAttribute("postParams", postParams);
84+
/**
85+
*
86+
* The payment may now be completed using the received & validated
87+
* token.
88+
*
89+
* For demonstration purposes, all post parameters are added to the view
90+
* model to display data received from cardholder's browser.
91+
*/
9092

93+
model.addAttribute("postParams", postParams);
9194
return "receipt";
9295
}
9396

94-
}
97+
}

java/spring-boot/src/main/java/com/cybersource/flex/application/EntryPoint.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
/**
2-
* Copyright (c) 2016 by CyberSource
3-
* Governing licence: https://github.com/CyberSource/cybersource-flex-samples/blob/master/LICENSE.md
4-
*/
5-
2+
* Copyright (c) 2016 by CyberSource
3+
* Governing licence: https://github.com/CyberSource/cybersource-flex-samples/blob/master/LICENSE.md
4+
*/
65
package com.cybersource.flex.application;
76

87
import org.springframework.boot.SpringApplication;
98
import org.springframework.boot.autoconfigure.SpringBootApplication;
109

11-
import java.lang.reflect.Field;
12-
1310
@SpringBootApplication
1411
public class EntryPoint {
1512

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* Copyright (c) 2016 by CyberSource
3+
* Governing licence: https://github.com/CyberSource/cybersource-flex-samples/blob/master/LICENSE.md
4+
*/
5+
package com.cybersource.flex.application;
6+
7+
import java.security.InvalidKeyException;
8+
import java.security.KeyFactory;
9+
import java.security.NoSuchAlgorithmException;
10+
import java.security.PublicKey;
11+
import java.security.Signature;
12+
import java.security.SignatureException;
13+
import java.security.spec.InvalidKeySpecException;
14+
import java.security.spec.X509EncodedKeySpec;
15+
import java.util.Base64;
16+
import org.springframework.stereotype.Service;
17+
18+
@Service
19+
public class FlexSecurityService {
20+
21+
private static final Base64.Decoder DECODER = Base64.getDecoder();
22+
23+
public PublicKey decodePublicKey(String derEncodedKey) {
24+
try {
25+
byte[] keyBytes = DECODER.decode(derEncodedKey);
26+
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(keyBytes));
27+
return publicKey;
28+
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
29+
throw new RuntimeException(e);
30+
}
31+
}
32+
33+
public boolean verify(final PublicKey publicKey, final String dataToVerify, final String base64Signature) {
34+
try {
35+
final Signature signInstance = Signature.getInstance("SHA512withRSA");
36+
signInstance.initVerify(publicKey);
37+
signInstance.update(dataToVerify.getBytes());
38+
byte[] signature = DECODER.decode(base64Signature);
39+
return signInstance.verify(signature);
40+
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException ex) {
41+
throw new RuntimeException(ex);
42+
}
43+
}
44+
45+
}

0 commit comments

Comments
 (0)