This version of the SDK has been deprecated and replaced with the newly reconstructed SDK.
For the latest API features and improved integration, please visit our updated repository at https://github.com/AfterShip/tracking-sdk-java and follow the provided instructions.
The Java SDK of AfterShip API, please see API documentation in: https://www.aftership.com/docs/api/4
Requirements:
- JDK 1.8 or superior.
<dependency>
<groupId>com.aftership</groupId>
<artifactId>aftership-sdk</artifactId>
<version>4.0.1</version>
</dependency>
implementation "com.aftership:aftership-sdk:4.0.1"
The following code example shows the three main steps to use aftership-sdk-java:
- Create
AfterShipObject.
AftershipOptionoption = newAftershipOption();
option.setEndpoint("https://api.aftership.com/tracking/2023-10");
AfterShipafterShip = newAfterShip("YOUR_API_KEY", option);
// if add aes signAfterShipafterShip = newAfterShip("YOUR_API_KEY", AuthenticationType.AES, "YOUR_API_SECRET", option);- Get the Endpoint Interface and call the method, then return the object.
CourierList courierList = afterShip.getCourierEndpoint().listCouriers();
- Handling
DataorAftershipExceptionorRateLimit
try {
AftershipOptionoption = newAftershipOption();
option.setEndpoint("https://api.aftership.com/tracking/2023-10");
AfterShipafterShip = newAfterShip("YOUR_API_KEY", option);
CourierListcourierList = afterShip.getCourierEndpoint().listCouriers();
// using dataSystem.out.println(courierList);
} catch (SdkException | RequestExceptione) {
// handle SdkException, RequestExceptionSystem.out.println(e.getType());
System.out.println(e.getMessage());
System.out.println(e.getData());
} catch (ApiExceptione) {
// handle ApiExceptionif (e.isTooManyRequests()) {
// Analyze RateLimit when TooManyRequests occurSystem.out.println(e.getRateLimit().getReset());
System.out.println(e.getRateLimit().getLimit());
System.out.println(e.getRateLimit().getRemaining());
return;
}
System.out.println(e.getType());
System.out.println(e.getCode());
System.out.println(e.getMessage());
}The SSL Protocol of
api.aftership.comis TLSV1.2, and the Cipher is AES256-SHA. It is a known issue due to this reason:
You can check the CipherSuite according to this article
Execute java CipherSuite api.aftership.com:443, under normal circumstances, it should output
CipherSuite used on the handshake:TLS_AES_256_GCM_SHA384
Otherwise, you should check the cipherSuite of your JVM.
- Make sure
crypto.policy=unlimitedis effective in the jre/lib/securityjava.security - If your JDK version number is lower than 7u171 or 8u161, please follow this oracle article to download JCE.
You can check the protocol version used during the SSL negotiation by capturing transport packet
There are 4 kinds of exception
- AftershipException
- SdkException
- RequestException
- ApiException
Error object of this SDK contain fields:
type- Require - type of the error, please handle each error by this fieldmessage- Optional - detail message of the errorcode- Optional - error code for API ErrorYou can find tips for Aftership's error codes in here: https://www.aftership.com/docs/tracking/quickstart/request-errors
If it's Aftership's API Error, get code to confirm the cause of the error:
catch (AftershipExceptione){ if(e.isApiError()){ System.out.println(e.getCode()); } }
data- Optional - data lead to the errorThe debug Data is a
Map<String, Object>object that can get the call parameters.The following data may be available:
catch (AftershipException e){ System.out.println(e.getData()); // or System.out.println(e.getData().get(DEBUG_DATA_KEY_REQUEST_CONFIG)); System.out.println(e.getData().get(DEBUG_DATA_KEY_REQUEST_HEADERS)); System.out.println(e.getData().get(DEBUG_DATA_KEY_REQUEST_DATA)); System.out.println(e.getData().get(DEBUG_DATA_KEY_RESPONSE_BODY)); }
AftershipException is the base class for all exception classes and can capture it for uniform handling.
See the Rate Limiter section for TooManyRequests in ApiException.
catch (AftershipExceptione){
if(e.isApiError()){
System.out.println(e.getCode());
if(e.isTooManyRequests() && einstanceofApiException){
System.out.println(((ApiException)e).getRateLimit());
}
}
System.out.println(e.getType());
System.out.println(e.getMessage());
System.out.println(e.getData());
}Exception return by the SDK instance, mostly invalid param type when calling constructor or endpoint method error.Type is one of ErrorTypeThrow by the SDK instance
try {
AftershipOptionoption = newAftershipOption();
option.setEndpoint("https://api.aftership.com/tracking/2023-10");
AfterShipafterShip =
newAfterShip(null, option);
} catch (SdkExceptione) {
System.out.println(e.getMessage());
}
/* ConstructorError: Invalid API key; type: ConstructorError */Throw by endpoint method
try {
AftershipOptionoption = newAftershipOption();
option.setEndpoint("https://api.aftership.com/tracking/2023-10");
AfterShipafterShip =
newAfterShip("YOUR_API_KEY", option);
afterShip.getTrackingEndpoint().getTracking("", null);
} catch (SdkExceptione) {
System.out.println(e.getMessage());
}
/* ConstructorError: Required tracking id; type: ConstructorError */Error return by the request module
error.Type could be HandlerError, etc.
try {
AftershipOptionoption = newAftershipOption();
option.setEndpoint("https://api.aftership.com/tracking/2023-10");
AfterShipafterShip =
newAfterShip("YOUR_API_KEY", option);
afterShip.getTrackingEndpoint().getTracking("abc", null);
} catch (RequestExceptione) {
System.out.println(e.getMessage());
}
/* null; type: HandlerError; */Error return by the AfterShip APIerror.Type should be the same as https://www.aftership.com/docs/api/4/errors
try {
AftershipOptionoption = newAftershipOption();
option.setEndpoint("https://api.aftership.com/tracking/2023-10");
AfterShipafterShip =
newAfterShip("YOUR_API_KEY", option);
afterShip.getTrackingEndpoint().getTracking("abc", null);
} catch (ApiExceptione) {
System.out.println(e.getMessage());
}
/* The value of `id` is invalid.; type: BadRequest; code: 4015; */To understand AfterShip rate limit policy, please see limit session in https://www.aftership.com/docs/api/4
You can get the recent rate limit by ApiException.getRateLimit().
try {
AftershipOptionoption = newAftershipOption();
option.setEndpoint("https://api.aftership.com/tracking/2023-10");
AfterShipafterShip =
newAfterShip("YOUR_API_KEY", option);
afterShip.getCourierEndpoint().listCouriers();
} catch (SdkException | RequestExceptione) {
System.out.println(e.getType());
} catch (ApiExceptione) {
if (e.isTooManyRequests()) {
System.out.println(e.getRateLimit().getReset());
System.out.println(e.getRateLimit().getLimit());
System.out.println(e.getRateLimit().getRemaining());
}
}
// 1589869159// 10// 9When creating an Aftership object, you can define the timeout time for http requests, Of course, use the default value of 20 seconds when not set. The unit is milliseconds.
AftershipOption option = new AftershipOption();
option.setEndpoint("https://api.aftership.com/tracking/2023-10");
option.setCallTimeout(10 * 1000);
option.setConnectTimeout(10 * 1000);
option.setReadTimeout(10 * 1000);
option.setWriteTimeout(10 * 1000);
AfterShip afterShip = new AfterShip(SampleUtil.getApiKey(), option);
Retry policy implemented using OKHttp interceptor. Allows to customize the delay interval, maximum retry interval, and number of retries; implements an exponential backoff delay strategy.
publicclassRetryInterceptorimplementsInterceptor {
privatelonginitialDelay; // Initial retry delay in millisecondsprivatelongmaxDelay; // Maximum retry delay in millisecondsprivateintmaxRetries; // Maximum number of retriesprivatedoublebackoffMultiplier; // Exponential backoff multiplier. Should ideally be greater than 1.privatedoublejitterFactor; // Jitter factor for randomized delayprivateList<RetryCondition> retryConditions; // Customizable retry condition
}initialDelay: The initial retry interval. Default is500ms.maxDelay: The maximum delay. After reaching the maximum delay, subsequent retries will use the same value. Default is18s(default timeout is 20s).maxRetries: The maximum number of retries. Default is10.backoffMultiplier: The exponential backoff multiplier. After each retry attempt, the retry interval will be multiplied by this value, resulting in exponential growth of delays. Default is1.6.jitterFactor: The jitter factor. Adds positive or negative jitter to calculated exponential backoff delay, helping to distribute retry requests and reduce server load. Default is0.2.retryConditions: Custom retry conditions allow customers to define their own retry criteria. Default is to retry on exceptions or when response status code is≥500.
Calculates the exponential backoff delay based on the number of retry attempts.
publicclassRetryInterceptorimplementsInterceptor {
/** * Calculates the exponential backoff delay for the specified retry attempt number, using the configured * backoff multiplier and maximum delay, and adds randomized jitter to the delay. * * @param retries The current retry attempt number. * @return The calculated delay before the next retry attempt, in milliseconds. */privatelongbackoff(intretries) {
// Calculate exponential backoff delay: initialDelay * (backoffMultiplier ^ retries)longdelay = (long) (initialDelay * Math.pow(backoffMultiplier, retries));
// Calculate jitter: jitterFactor * delay * random number in [-1, 1) rangedoublejitter = jitterFactor * delay * (Math.random() - 0.5) * 2;
// Add the exponential backoff delay and jitter to get the final delaylongfinalDelay = (long) (delay + jitter);
// Ensure the final delay does not exceed the maximum delayif (finalDelay > maxDelay) {
finalDelay = maxDelay;
}
returnfinalDelay;
}Assuming retries=10, initialDelay=500, backoffMultiplier=1.6, jitterFactor=0.2, and maxDelay=18,000; the time intervals required for each retry are as follows:
| retries | delay |
|---|---|
| 1 | 586 |
| 2 | 787 |
| 3 | 1342 |
| 4 | 2304 |
| 5 | 3219 |
| 6 | 4592 |
| 7 | 7293 |
| 8 | 12020 |
| 9 | 18000 |
| 10 | 18000 |
publicclassRetrySample {
publicstaticvoidmain(String[] args) {
AftershipOptionoption = SampleUtil.getAftershipOption();
RetryOptionretryOption = newRetryOption();
retryOption.setRetryDelay(500);
retryOption.setRetryMaxDelay(18 * 1000L);
retryOption.setRetryCount(10);
retryOption.setRetryConditions(Arrays.asList((
(response, exception) -> exception != null || (response != null && response.code() >= 500))));
option.setRetryOption(retryOption);
AfterShipafterShip = newAfterShip(SampleUtil.getApiKey(), option);
}
}- Retry interval: 500ms
- Maximum retry interval: 18s
- Number of retries: 10
- Retry conditions: exception != null || (response != null && response.code() >= 500)
- backoffMultiplier and jitterFactor are set to 1.6 and 0.2 respectively by default
We recommend using only one AfterShip object to request interfaces of the API, and calling the shutdown method if you want to completely clean up all network resources when you shut down your system. In other cases, not running shutdown method has no effect.
try {
afterShip.getCourierEndpoint().listCouriers();
} catch (RequestException | ApiExceptione) {
e.printStackTrace();
} finally {
try {
afterShip.shutdown();
} catch (IOExceptione) {
e.printStackTrace();
}
}try {
CourierList courierList = afterShip.getCourierEndpoint().listCouriers();
System.out.println(courierList.getTotal());
System.out.println(courierList.getCouriers().get(0).getName());
} catch (AftershipException e) {
System.out.println(e.getMessage());
}
try {
CourierList courierList = afterShip.getCourierEndpoint().listAllCouriers();
System.out.println(courierList.getTotal());
System.out.println(courierList.getCouriers());
} catch (AftershipException e) {
System.out.println(e.getMessage());
}
CourierDetectTrackingtracking = newCourierDetectTracking();
tracking.setTrackingNumber("906587618687");
CourierDetectRequestcourierDetectRequest = newCourierDetectRequest(tracking);
try {
CourierDetectListcourierDetectList =
afterShip.getCourierEndpoint().detectCouriers(courierDetectRequest.getTracking());
System.out.println(courierDetectList.getTotal());
System.out.println(courierDetectList.getCouriers());
} catch (AftershipExceptione) {
System.out.println(e.getMessage());
}NewTrackingnewTracking = newNewTracking();
// slug from listAllCouriers()newTracking.setSlug(newString[] {"acommerce"});
newTracking.setTrackingNumber("1234567890");
newTracking.setTitle("Title Name");
newTracking.setSmses(newString[] {"+18555072509", "+18555072501"});
newTracking.setEmails(newString[] {"email@yourdomain.com", "another_email@yourdomain.com"});
newTracking.setOrderId("ID 1234");
newTracking.setOrderIdPath("http://www.aftership.com/order_id=1234");
newTracking.setCustomFields(
newHashMap<String, String>(2) {
{
put("product_name", "iPhone Case");
put("product_price", "USD19.99");
}
});
newTracking.setLanguage("en");
newTracking.setOrderPromisedDeliveryDate("2019-05-20");
newTracking.setDeliveryType("pickup_at_store");
newTracking.setPickupLocation("Flagship Store");
newTracking.setPickupNote(
"Reach out to our staffs when you arrive our stores for shipment pickup");
try {
Trackingtracking = afterShip.getTrackingEndpoint().createTracking(newTracking);
System.out.println(tracking);
} catch (AftershipExceptione) {
System.out.println(e.getMessage());
}String id = "u2qm5uu9xqpwykaqm8d5l010";
try {
Tracking tracking = afterShip.getTrackingEndpoint().deleteTracking(id);
System.out.println(tracking);
} catch (AftershipException e) {
System.out.println(e.getMessage());
}
GetTrackingsParams optionalParams = new GetTrackingsParams();
optionalParams.setFields("title,order_id");
optionalParams.setLang("china-post");
optionalParams.setLimit(10);
try {
PagedTrackings pagedTrackings = afterShip.getTrackingEndpoint().getTrackings(optionalParams);
System.out.println(pagedTrackings);
} catch (AftershipException e) {
System.out.println(e.getMessage());
}
Get by id:
Stringid = "l389dilsluk9ckaqmetr901y"; try { Trackingtracking = afterShip.getTrackingEndpoint().getTracking(id, null); System.out.println(tracking); } catch (AftershipExceptione) { System.out.println(e.getMessage()); }
Get by slug and tracking_number:
String slug = "acommerce"; String trackingNumber = "1234567890"; try { Tracking tracking = afterShip .getTrackingEndpoint() .getTracking(new SlugTrackingNumber(slug, trackingNumber), null); System.out.println(tracking); } catch (AftershipException e) { System.out.println(e.getMessage()); }
Stringid = "vebix4hfu3sr3kac0epve01n";
UpdateTrackingupdateTracking = newUpdateTracking();
updateTracking.setTitle("title123");
try {
Trackingtracking1 = afterShip.getTrackingEndpoint().updateTracking(id, updateTracking);
System.out.println(tracking1);
} catch (AftershipExceptione) {
System.out.println(e.getMessage());
}Stringid = "l389dilsluk9ckaqmetr901y";
try {
Trackingtracking = afterShip.getTrackingEndpoint().reTrack(id);
System.out.println(tracking);
} catch (AftershipExceptione) {
System.out.println(e.getMessage());
}String id = "5b7658cec7c33c0e007de3c5";
try {
Tracking tracking =
afterShip.getTrackingEndpoint().markAsCompleted(id, new CompletedStatus(ReasonKind.LOST));
System.out.println(tracking);
} catch (AftershipException e) {
System.out.println(e.getMessage());
}
Stringid = "vebix4hfu3sr3kac0epve01n";
GetCheckpointParamoptionalParam = newGetCheckpointParam();
optionalParam.setFields(FieldsKind.combine(FieldsKind.TAG));
optionalParam.setLang(LangKind.CHINA_EMS);
try {
LastCheckpointlastCheckpoint =
afterShip.getCheckpointEndpoint().getLastCheckpoint(id, optionalParam);
System.out.println(lastCheckpoint.getSlug());
System.out.println(lastCheckpoint.getTrackingNumber());
System.out.println(lastCheckpoint.getCheckpoint());
} catch (AftershipExceptione) {
System.out.println(e.getMessage());
}String id = "vebix4hfu3sr3kac0epve01n";
try {
Notification notification = afterShip.getNotificationEndpoint().getNotification(id);
System.out.println(notification);
} catch (AftershipException e) {
System.out.println(e.getMessage());
}
Stringid = "vebix4hfu3sr3kac0epve01n";
NotificationaddNotification = newNotification();
addNotification.setSmses(newString[] {"+85261236123", "Invalid Mobile Phone Number"});
try {
Notificationnotification =
afterShip.getNotificationEndpoint().addNotification(id, addNotification);
System.out.println(notification);
} catch (AftershipExceptione) {
System.out.println(e.getMessage());
}String id = "vebix4hfu3sr3kac0epve01n";
Notification removeNotification = new Notification();
removeNotification.setEmails(new String[] {"invalid EMail @ Gmail. com"});
removeNotification.setSmses(new String[] {"+85261236123"});
try {
Notification notification =
afterShip.getNotificationEndpoint().removeNotification(id, removeNotification);
System.out.println(notification);
} catch (AftershipException e) {
System.out.println(e.getMessage());
}
getTracking("l389dilsluk9ckaqmetr901y", null);
getTracking(new SlugTrackingNumber("acommerce", "1234567890"), null);
Copyright (c) 2015-2022 Aftership
Licensed under the MIT license.