Skip to content

Commit

Permalink
Amazon Pay API SDK (Java) 2.6.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Nayan Kumar S committed Nov 16, 2023
1 parent 3ff564e commit d51436b
Show file tree
Hide file tree
Showing 18 changed files with 807 additions and 91 deletions.
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
### Version 2.6.1 - November 2023
* Introducing new Merchant Onboarding & Account Management APIs, which allows our partners to onboard merchants programatically and as part of account management offer them creation, updation and deletion/dissociation capability.
* Fixed connection pooling issue to enhance stability and performance.
* Corrected README.md file.
* Enable client request to configure connection, connect and read timeout.
* Fixed Security risk

### Version 2.6.0 - March 2023
* Introducing new v2 Reporting APIs. Reports allow you to retrieve consolidated data about Amazon Pay transactions and settlements. In addition to managing and downloading reports using Seller Central, Amazon Pay offers APIs to manage and retrieve your reports.
* Introducing new signature generation algorithm AMZN-PAY-RSASSA-PSS-V2 & increasing salt length from 20 to 32.
Expand Down
58 changes: 44 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ To use the SDK in a Maven project, add a <dependency> reference in your pom.xml
<dependency>
<groupId>software.amazon.pay</groupId>
<artifactId>amazon-pay-api-sdk-java</artifactId>
<version>2.6.0</version>
<version>2.6.1</version>
</dependency>
</dependencies>
```

To use the SDK in a Gradle project, add the following line to your build.gradle file::

```
implementation 'software.amazon.pay:amazon-pay-api-sdk-java:2.6.0'
implementation 'software.amazon.pay:amazon-pay-api-sdk-java:2.6.1'
```

For legacy projects, you can just grab the binary [jar file](https://github.com/amzn/amazon-pay-api-sdk-java/releases) from the GitHub Releases page.
Expand Down Expand Up @@ -185,6 +185,19 @@ try {
} catch (AmazonPayClientException e) {
e.printStackTrace();
}

// If you want to set client request configuration, connection, connect and socket timeout, the payConfiguration looks like below:

try {
payConfiguration = new PayConfiguration()
.setPublicKeyId("YOUR_PUBLIC_KEY_ID")
.setRegion(Region.YOUR_REGION_CODE)
.setPrivateKey("YOUR_PRIVATE_KEY_STRING".toCharArray())
.setEnvironment(Environment.SANDBOX)
.setRequestConfig(new RequestConfig(1000, 2000, 4000);//connection timeout = 1s, connect timeout = 2s, socket timeout = 4s
} catch (AmazonPayClientException e) {
e.printStackTrace();
}
```

# Convenience Functions (Overview)
Expand Down Expand Up @@ -238,6 +251,11 @@ Please note that your solution provider account must have a pre-existing relatio

* AmazonPayClient: **getAuthorizationToken**(String mwsAuthToken, String merchantId[, Map<String, String> header]) &#8594; GET to "$version/authorizationTokens/$mwsAuthToken?merchantId=$merchantId"

### Amazon Checkout v2 Merchant Onboarding & Account Management object
* WebstoreClient: **registerAmazonPayAccount**(JSONObject payload[, Map<String, String> header]) &#8594; POST to "$version/merchantAccounts"
* WebstoreClient: **updateAmazonPayAccount**(String merchantAccountId, JSONObject payload[, Map<String, String> header]) &#8594; PATCH to "$version/merchantAccounts/$merchantAccountId"
* WebstoreClient: **deleteAmazonPayAccount**(String merchantAccountId[, Map<String, String> header]) &#8594; DELETE to "$version/merchantAccounts/$merchantAccountId"

# Using Convenience Functions

Four quick steps are needed to make an API call:
Expand Down Expand Up @@ -336,7 +354,7 @@ JSONObject payload = new JSONObject();
JSONObject webCheckoutDetails = new JSONObject();
webCheckoutDetails.put("checkoutReviewReturnUrl", "https://localhost/store/checkout_review");
payload.put("webCheckoutDetails", webCheckoutDetails);
payload.put("storeId", "amzn1.application-oa2-client.4c46698afa4d4b23b645d05762fc78fa");
payload.put("storeId", "amzn1.application-oa2-client.000000000000000000000000000000000");
AmazonPayResponse response = null;
String checkoutSessionId = null;
Expand All @@ -355,6 +373,7 @@ try {
```java
AmazonPayResponse response = null;
String checkoutSessionId = "00000000-0000-0000-0000-000000000000";
try {
response = webstoreClient.getCheckoutSession(checkoutSessionId);
Expand All @@ -369,6 +388,8 @@ try {
```java
AmazonPayResponse response = null;
String checkoutSessionId = "00000000-0000-0000-0000-000000000000";
JSONObject payload = new JSONObject();
JSONObject updateWebCheckoutDetails = new JSONObject();
updateWebCheckoutDetails.put("checkoutResultReturnUrl", "https://localhost/store/checkout_return");
Expand Down Expand Up @@ -403,14 +424,13 @@ try {
```java
AmazonPayResponse response = null;
JSONObject payload = new JSONObject();
String checkoutSessionId = "00000000-0000-0000-0000-000000000000";
JSONObject paymentDetails = new JSONObject();
JSONObject payload = new JSONObject();
JSONObject chargeAmount = new JSONObject();
chargeAmount.put("amount", "12.34");
chargeAmount.put("amount", "14.00");
chargeAmount.put("currencyCode", "USD");
paymentDetails.put("chargeAmount", chargeAmount);
payload.put("paymentDetails", paymentDetails);
payload.put("chargeAmount", chargeAmount);
try {
response = webstoreClient.completeCheckoutSession(checkoutSessionId, payload);
Expand All @@ -425,6 +445,7 @@ try {
```java
AmazonPayResponse response = null;
String chargePermissionId = "S01-0000000-0000000";
try {
response = webstoreClient.getChargePermissions(chargePermissionId);
Expand All @@ -439,6 +460,8 @@ try {
```java
AmazonPayResponse response = null;
String chargePermissionId = "S01-0000000-0000000";
JSONObject payload = new JSONObject();
JSONObject merchantMetadata = new JSONObject();
merchantMetadata.put("merchantReferenceId", "32-41-323141");
Expand All @@ -460,6 +483,8 @@ try {
```java
AmazonPayResponse response = null;
String chargePermissionId = "S01-0000000-0000000";
JSONObject payload = new JSONObject();
payload.put("closureReason", "Specify the reason here");
payload.put("cancelPendingCharges", "false");
Expand All @@ -477,9 +502,10 @@ try {
```java
AmazonPayResponse response = null;
String chargeId = "S01-0000000-0000000-C000000";
try {
response = webstoreClient.getCharge(chargesId);
response = webstoreClient.getCharge(chargeId);
} catch (AmazonPayClientException e) {
e.printStackTrace();
}
Expand All @@ -497,7 +523,7 @@ JSONObject chargeAmount = new JSONObject();
chargeAmount.put("amount", "1.23");
chargeAmount.put("currencyCode", "USD");
payload.put("chargePermissionId", "S01-3152594-4330637");
payload.put("chargePermissionId", "S01-0000000-0000000");
payload.put("chargeAmount", chargeAmount);
payload.put("captureNow", false);
// if payload.put("captureNow", true);
Expand All @@ -524,6 +550,7 @@ chargeId = response.getResponse().getString("chargeId");
```java
AmazonPayResponse response = null;
String chargeId = "S01-0000000-0000000-C000000";
JSONObject payload = new JSONObject();
JSONObject captureAmount = new JSONObject();
Expand All @@ -536,7 +563,7 @@ Map<String, String> header = new HashMap<String, String>();
header.put("x-amz-pay-idempotency-key", UUID.randomUUID().toString().replace("-", ""));
try {
response = webstoreClient.captureCharge(chargesId, payload, header);
response = webstoreClient.captureCharge(chargeId, payload, header);
} catch (AmazonPayClientException e) {
e.printStackTrace();
}
Expand All @@ -548,6 +575,7 @@ try {
```java
AmazonPayResponse response = null;
String chargeId = "S01-0000000-0000000-C000000";
JSONObject payload = new JSONObject();
payload.put("cancellationReason", "Buyer changed their mind");
Expand All @@ -565,6 +593,7 @@ try {
```java
AmazonPayResponse response = null;
String chargeId = "S01-0000000-0000000-C000000";
JSONObject payload = new JSONObject();
JSONObject refundAmount = new JSONObject();
Expand Down Expand Up @@ -592,6 +621,7 @@ refundId = response.getResponse().getString("refundId");
```java
AmazonPayResponse response = null;
String refundId = "S01-0000000-0000000-R000000";
try {
response = webstoreClient.getRefund(refundId);
Expand Down Expand Up @@ -800,7 +830,7 @@ List<String> processingStatuses = new ArrayList<>();
processingStatuses.add("COMPLETED");
queryParameters.put("reportTypes", reportTypes);
queryParameters.put("reportTypes", processingStatuses);
queryParameters.put("processingStatuses", processingStatuses);
try {
response = webstoreClient.getReports(queryParameters);
Expand All @@ -824,7 +854,7 @@ try {
## Amazon Checkout v2 Reporting APIs - GetReportDocument API
```java
AmazonPayResponse response = null;
String reportDocumentId = "1234567890";
String reportDocumentId = "amzn1.tortuga.0.000000000-0000-0000-0000-000000000000.00000000000000";
try {
response = webstoreClient.getReportDocument(reportDocumentId);
Expand Down Expand Up @@ -902,4 +932,4 @@ try {
} catch (AmazonPayClientException e) {
e.printStackTrace();
}
```
```
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
<groupId>software.amazon.pay</groupId>
<artifactId>amazon-pay-api-sdk-java</artifactId>
<packaging>jar</packaging>
<version>2.6.0</version>
<version>2.6.1</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.json/json -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20210307</version>
<version>20231013</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
Expand Down
118 changes: 110 additions & 8 deletions src/com/amazon/pay/api/AmazonPayClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.pool.PoolStats;
import org.apache.http.util.EntityUtils;
import org.apache.commons.lang.StringUtils;

Expand All @@ -46,10 +49,14 @@
public class AmazonPayClient {
final protected PayConfiguration payConfiguration;
final protected RequestSigner requestSigner;
final protected PoolingHttpClientConnectionManager connectionManager;

public AmazonPayClient(final PayConfiguration payConfiguration) throws AmazonPayClientException {
this.payConfiguration = payConfiguration;
requestSigner = new RequestSigner(payConfiguration);
this.connectionManager = new PoolingHttpClientConnectionManager();
this.connectionManager.setMaxTotal(payConfiguration.getClientConnections());
this.connectionManager.setDefaultMaxPerRoute(payConfiguration.getClientConnections());
}

/**
Expand Down Expand Up @@ -199,9 +206,9 @@ private AmazonPayResponse processRequest(final URI uri,
// Check for service errors
while (ServiceConstants.serviceErrors.containsValue(statusCode) &&
retry < payConfiguration.getMaxRetries()) {
retry++;
//retry request maxRetries number of times
long waitTime = Util.getExponentialWaitTime(retry);
long waitTime = payConfiguration.getRetryStrategy().getWaitTime(retry, statusCode);
retry++;
Thread.sleep(waitTime);

response = sendRequest(uri, postSignedHeaders, payload, httpMethodName);
Expand Down Expand Up @@ -246,8 +253,7 @@ private List<String> sendRequest(final URI uri,
String requestId = null;
int responseCode = 0;
try (final CloseableHttpClient client = Optional.ofNullable(payConfiguration.getProxySettings()).isPresent()
? Util.getCloseableHttpClientWithProxy(payConfiguration.getProxySettings(), payConfiguration)
: Util.getHttpClientWithConnectionPool(payConfiguration)) {
? getClosableHttpClientWithPoolAndProxy() : getClosableHttpClientWithConnectionPool()) {
final HttpUriRequest httpUriRequest = Util.getHttpUriRequest(uri, httpMethodName, payload);
for (Map.Entry<String, String> entry : headers.entrySet()) {
httpUriRequest.addHeader(entry.getKey(), entry.getValue());
Expand All @@ -257,11 +263,15 @@ private List<String> sendRequest(final URI uri,
if (responseCode < HttpURLConnection.HTTP_BAD_REQUEST) {
requestId = responses.getFirstHeader(ServiceConstants.X_AMZ_PAY_REQUEST_ID).toString();
String inputLine;
try (final BufferedReader in = new BufferedReader(
new InputStreamReader(responses.getEntity().getContent(), Util.DEFAULT_ENCODING))) {
while ((inputLine = in.readLine()) != null) {
response.append(inputLine).append(System.lineSeparator());
if(Optional.ofNullable(responses.getEntity()).isPresent()) {
try (final BufferedReader in = new BufferedReader(
new InputStreamReader(responses.getEntity().getContent(), Util.DEFAULT_ENCODING))) {
while ((inputLine = in.readLine()) != null) {
response.append(inputLine).append(System.lineSeparator());
}
}
} else {
response.append("{}").append(System.lineSeparator());
}
} else {
response.append(EntityUtils.toString(responses.getEntity()));
Expand All @@ -275,4 +285,96 @@ private List<String> sendRequest(final URI uri,
return result;
}

/**
* Helper function to retrieve the Connection Pool Stats to the caller to monitor the Connection Pool Performance
*
* @return a ConnectionPoolStats of the Connection Pool
*/
public ConnectionPoolStats getPoolStats() {
final PoolStats poolStats = this.connectionManager.getTotalStats();
final ConnectionPoolStats connectionPoolStats = new ConnectionPoolStats(poolStats.getMax(),
poolStats.getAvailable(), poolStats.getPending(), poolStats.getLeased());
return connectionPoolStats;
}

/**
* Returns the CloseableHttpClient object with Connection Pool based on the Payconfiguration
*
* @return the CloseableHttpClient
*/
protected CloseableHttpClient getClosableHttpClientWithConnectionPool() {
final HttpClientBuilder httpClientBuilder = HttpClients.custom()
.setConnectionManager(connectionManager)
.setConnectionManagerShared(true);
Util.applyRequestConfig(httpClientBuilder, this.payConfiguration);
return httpClientBuilder.build();
}

/**
* Returns the CloseableHttpClient object with Connection Pool based on the given proxy settings.
*
* @return the CloseableHttpClient
*/
protected CloseableHttpClient getClosableHttpClientWithPoolAndProxy() {
return Util.getHttpClientBuilderWithProxy(this.payConfiguration.getProxySettings(), this.payConfiguration)
.setConnectionManager(connectionManager)
.setConnectionManagerShared(true)
.build();
}

// ----------------------------------- Merchant Onboarding & Account Management APIs --------------------

/**
* Creates a non-logginable account for your merchant partners. These would be special accounts through which Merchants would not be able to login to Amazon or access Seller Central.
*
* @param payload JSONObject request body
* @param header Map&lt;String, String&gt; containing key-value pair of required headers (e.g., keys such as x-amz-pay-idempotency-key, x-amz-pay-authtoken).
* @return The response from registerAmazonPayAccount API, as returned by Amazon Pay.
* @throws AmazonPayClientException When an error response is returned by Amazon Pay due to bad request or other issue.
*/
public AmazonPayResponse registerAmazonPayAccount(final JSONObject payload, final Map<String, String> header) throws AmazonPayClientException {
final URI registerAmazonPayAccountURI = Util.getServiceURI(payConfiguration, ServiceConstants.ACCOUNT_MANAGEMENT);
return callAPI(registerAmazonPayAccountURI, "POST", null, payload.toString(), header);
}

public AmazonPayResponse registerAmazonPayAccount(final JSONObject payload) throws AmazonPayClientException {
return registerAmazonPayAccount(payload, null);
}

/**
* Updates a merchant account for the given Merchant Account ID. We would be allowing our partners to update only a certain set of fields which won’t change the legal business entity itself.
*
* @param merchantAccountId Internal Merchant Account ID provided while calling the API.
* @param payload JSONObject request body
* @param header Map&lt;String, String&gt; containing key-value pair of required headers (e.g., keys such as x-amz-pay-idempotency-key, x-amz-pay-authtoken).
* @return The response from updateAmazonPayAccount API, as returned by Amazon Pay.
* @throws AmazonPayClientException When an error response is returned by Amazon Pay due to bad request or other issue.
*/
public AmazonPayResponse updateAmazonPayAccount(final String merchantAccountId, final JSONObject payload, final Map<String, String> header) throws AmazonPayClientException {
final URI updateAmazonPayAccountURI = Util.getServiceURI(payConfiguration, ServiceConstants.ACCOUNT_MANAGEMENT);
final URI updateAmazonPayAccountFinalURI = updateAmazonPayAccountURI.resolve(updateAmazonPayAccountURI.getPath() + "/" + merchantAccountId);
return callAPI(updateAmazonPayAccountFinalURI, "PATCH", null, payload.toString(), header);
}

public AmazonPayResponse updateAmazonPayAccount(final String merchantAccountId, final JSONObject payload) throws AmazonPayClientException {
return updateAmazonPayAccount(merchantAccountId, payload, null);
}

/**
* Deletes the Merchant account for the given Merchant Account ID. Partners can close the merchant accounts created for their merchant partners.
*
* @param merchantAccountId Internal Merchant Account ID provided while calling the API.
* @param header Map&lt;String, String&gt; containing key-value pair of required headers (e.g., keys such as x-amz-pay-idempotency-key, x-amz-pay-authtoken).
* @return The response from deleteAmazonPayAccount API, as returned by Amazon Pay.
* @throws AmazonPayClientException When an error response is returned by Amazon Pay due to bad request or other issue.
*/
public AmazonPayResponse deleteAmazonPayAccount(final String merchantAccountId, final Map<String, String> header) throws AmazonPayClientException {
final URI deleteAmazonPayAccountURI = Util.getServiceURI(payConfiguration, ServiceConstants.ACCOUNT_MANAGEMENT);
final URI deleteAmazonPayAccountFinalURI = deleteAmazonPayAccountURI.resolve(deleteAmazonPayAccountURI.getPath() + "/" + merchantAccountId);
return callAPI(deleteAmazonPayAccountFinalURI, "DELETE", null, "", header);
}

public AmazonPayResponse deleteAmazonPayAccount(final String merchantAccountId) throws AmazonPayClientException {
return deleteAmazonPayAccount(merchantAccountId, null);
}
}
Loading

0 comments on commit d51436b

Please sign in to comment.