I'm working on an integration project for a client that requires creating AusPost shipments using the eParcel REST API from a Delphi 10.2 Tokyo service program.
Here is what I have as the Constructor
for my class:
constructor TAusPostRESTServices.Create;
begin
// Create the REST objects
HTTPBasicAuthenticator := THTTPBasicAuthenticator.Create(API_KEY, API_PASSWORD);
RESTClient := TRESTClient.Create(BASE_URL);
RESTResponse := TRESTResponse.Create(nil);
RESTRequest := TRESTRequest.Create(nil);
// Set the initial properties
RESTClient.Params.Clear;
RESTClient.HandleRedirects := true;
RESTClient.Authenticator := HTTPBasicAuthenticator;
RESTClient.RaiseExceptionOn500 := false;
RESTRequest.Client := RESTClient;
RESTRequest.Response := RESTResponse;
RESTRequest.SynchronizedEvents := false;
end;
And the constants it uses (note that the account and API values are from the reference API and probably don't actually work).
const
BASE_URL = 'https://digitalapi.auspost.com.au/test';
ACCOUNT = '0000123456';
API_KEY = '601a4032-6dbd-46aa-9c6c-8c6dacca5e61';
API_PASSWORD = 'password';
The Uses
clause has at least these:
REST.Client, REST.Authenticator.Basic, IPPeerClient, JSON, REST.Types
The eParcel API methods I'm trying to use are Create Shipments and Get Shipments .
Here is what my Get Shipments Delphi code looks like (Don't worry about the AJSONValue
response. Still working on what that will do eventually):
function TAusPostRESTServices.GetShipments(var AError: String): Boolean;
var
AStatusCode: Integer;
AJSONString: String;
AJSONValue: TJSonValue;
AParameter: TRESTRequestParameter;
begin
// Set the request header info
RESTRequest.Method := TRESTRequestMethod.rmGET;
RESTRequest.Resource := '/shipping/v1/shipments';
RESTRequest.Accept := 'application/json';
AParameter := RESTRequest.Params.AddItem;
AParameter.ContentType := ctAPPLICATION_JSON;
AParameter.name := 'Content-Type';
AParameter.Value := 'application/json';
AParameter.Kind := TRESTRequestParameterKind.pkHTTPHEADER;
RESTRequest.Params.AddHeader('account-number', ACCOUNT);
try
try
RESTRequest.Execute;
except on E: Exception do
;
end;
// If the StatusCode is 200, then the REST method worked
AStatusCode := RESTRequest.Response.StatusCode;
if AStatusCode <> 200 then
AError := RESTRequest.Response.Content
else
begin
AJSONValue := TJSonObject.ParseJSONValue(RESTRequest.Response.Content);
AJSONValue.Free;
AError := '';
end;
finally
Result := (AError = '');
end;
end;
This works fine and returns the one trial demo shipment I created using Postman, from the sample JSON included in the AusPost API documentation.
{
"shipments": [
{
"shipment_id": "T7sK0EA9HGcAAAFufLYgUxkK",
"shipment_reference": "My second shipment ref",
"shipment_creation_date": "2019-10-30T06:42:42+11:00",
"customer_reference_1": "cb1234",
"customer_reference_2": "cb2345",
"sender_references": [
"cb1234",
"cb2345"
],
"from": {
"type": "MERCHANT_LOCATION",
"lines": [
"111 Bourke St"
],
"suburb": "Melbourne",
"postcode": "3000",
"state": "VIC",
"apcn": "2468013579",
"name": "Carl Block",
"country": "AU"
},
"to": {
"type": "STANDARD_ADDRESS",
"lines": [
"PO Box 123"
],
"suburb": "Rye",
"postcode": "3941",
"state": "VIC",
"apcn": "1234567890",
"name": "Blocked Carl",
"business_name": "In debt",
"country": "AU",
"email": "carl@gmai.co",
"phone": "0356567567",
"delivery_instructions": "please leave behind shed"
},
"items": [
{
"weight": 1,
"height": 10,
"length": 10,
"width": 10,
"contains_dangerous_goods": false,
"authority_to_leave": true,
"safe_drop_enabled": true,
"allow_partial_delivery": false,
"item_id": "3mQK0EA9CksAAAFui7YgUxkK",
"item_reference": "blocked",
"tracking_details": {
"article_id": "111JD538925401000930800",
"consignment_id": "111JD5389254"
},
"product_id": "7E55",
"item_summary": {
"total_cost": 11.48,
"total_cost_ex_gst": 11.48,
"total_gst": 0,
"status": "Created"
},
"item_contents": [],
"label": {
"status": "Pending",
"errors": []
},
"postage_details": {
"price": {
"calculated_price": 11.48,
"calculated_price_ex_gst": 11.48,
"calculated_gst": 0
}
}
},
{
"weight": 2.123,
"height": 10,
"length": 100.1,
"width": 10,
"contains_dangerous_goods": false,
"authority_to_leave": true,
"safe_drop_enabled": true,
"allow_partial_delivery": false,
"item_id": "9cgK0EA9smIAAAFufrYgUxkK",
"item_reference": "blocked",
"tracking_details": {
"article_id": "111JD538925403000930804",
"consignment_id": "111JD5389254"
},
"product_id": "7E55",
"item_summary": {
"total_cost": 23.34,
"total_cost_ex_gst": 23.34,
"total_gst": 0,
"status": "Created"
},
"item_contents": [],
"label": {
"status": "Pending",
"errors": []
},
"postage_details": {
"price": {
"calculated_price": 23.34,
"calculated_price_ex_gst": 23.34,
"calculated_gst": 0
}
}
},
{
"weight": 1,
"height": 10,
"length": 10,
"width": 10,
"contains_dangerous_goods": false,
"authority_to_leave": true,
"safe_drop_enabled": true,
"allow_partial_delivery": false,
"item_id": "_hEK0EA9zIIAAAFuirYgUxkK",
"item_reference": "blocked",
"tracking_details": {
"article_id": "111JD538925402000930807",
"consignment_id": "111JD5389254"
},
"product_id": "7E55",
"item_summary": {
"total_cost": 11.48,
"total_cost_ex_gst": 11.48,
"total_gst": 0,
"status": "Created"
},
"item_contents": [],
"label": {
"status": "Pending",
"errors": []
},
"postage_details": {
"price": {
"calculated_price": 11.48,
"calculated_price_ex_gst": 11.48,
"calculated_gst": 0
}
}
}
],
"options": {},
"shipment_summary": {
"total_cost": 47.41,
"total_cost_ex_gst": 43.1,
"fuel_surcharge": 1.11,
"total_gst": 4.31,
"status": "Created",
"tracking_summary": {
"Created": 3
},
"number_of_items": 3
},
"movement_type": "DESPATCH",
"charge_to_account": "1001746337"
}
],
"pagination": {
"total_number_of_records": 1,
"number_of_records_per_page": 1000,
"current_page_number": 1
}
}
My Create Shipment is almost identical:
function TAusPostRESTServices.CreateShipment(var AError: String): Boolean;
var
AStatusCode: Integer;
AJSONString: String;
AJSONValue: TJSonValue;
AParameter: TRESTRequestParameter;
begin
// Set the request header info
RESTRequest.Method := TRESTRequestMethod.rmPOST;
RESTRequest.Resource := '/shipping/v1/shipments';
RESTRequest.Accept := 'application/json';
AParameter := RESTRequest.Params.AddItem;
AParameter.ContentType := ctAPPLICATION_JSON;
AParameter.name := 'Content-Type';
AParameter.Value := 'application/json';
AParameter.Kind := TRESTRequestParameterKind.pkHTTPHEADER;
RESTRequest.Params.AddHeader('account-number', ACCOUNT);
try
AJSONString := '{'+
' "shipments": ['+
' {'+
' "shipment_reference": "139301",'+
' "customer_reference_1": "1",'+
' "from": {'+
' "name": "MERCHANT_LOCATION",'+
' "lines": ['+
' "111 Bourke St"'+
' ],'+
' "suburb": "MELBOURNE",'+
' "postcode": "3000",'+
' "state": "VIC"'+
' },'+
' "to": {'+
' "name": "STANDARD_ADDRESS",'+
' "lines": ['+
' "PO Box 123"'+
' ],'+
' "suburb": "Rye",'+
' "state": "VIC",'+
' "postcode": "3941",'+
' "delivery_instructions": "please leave behind shed",'+
' "email": "carl@gmai.co"'+
' },'+
' "items": ['+
' {'+
' "width": 10,'+
' "height": 10,'+
' "length": 10,'+
' "weight": 0.19,'+
' "product_id": "7E55",'+
' "authority_to_leave": true'+
' }'+
' ]'+
' }'+
' ]'+
'}';
RESTRequest.Body.Add(AJSONString, ctAPPLICATION_JSON);
try
RESTRequest.Execute;
except on E: Exception do
;
end;
// If the StatusCode is 200, then the REST method worked
AStatusCode := RESTRequest.Response.StatusCode;
if AStatusCode <> 200 then
AError := RESTRequest.Response.Content
else
begin
AJSONValue := TJSonObject.ParseJSONValue(RESTRequest.Response.Content);
AJSONValue.Free;
AError := '';
end;
finally
Result := (AError = '');
end;
end;
When this is run, the status code is 404 and the content text is
{
"code": "404",
"name": "Not Found",
"message": "Invalid HTTP method POST or request path /shipments. Please refer to the Developer Centre reference at https://developers.auspost.com.au/apis/shipping-and-tracking/reference for correct usage."
}
But if I setup that Create Shipment POST in Postman, with the Raw Body being the same JSON string, it works fine, and I get an appropriate response:
{
"shipments": [
{
"shipment_id": "QdkK0EA99JoAAAFuba0gUzkF",
"shipment_reference": "139301",
"shipment_creation_date": "2019-11-05T11:45:03+11:00",
"items": [
{
"weight": 0.190,
"authority_to_leave": true,
"safe_drop_enabled": true,
"allow_partial_delivery": true,
"item_id": "kwgK0EA9Es8AAAFubq0gUzkF",
"tracking_details": {
"article_id": "111JD539319101000931501",
"consignment_id": "111JD5393191"
},
"product_id": "7E55",
"item_summary": {
"total_cost": 17.14,
"total_cost_ex_gst": 17.14,
"total_gst": 0.00,
"status": "Created"
},
"item_contents": []
}
],
"options": {},
"shipment_summary": {
"total_cost": 17.55,
"total_cost_ex_gst": 15.95,
"fuel_surcharge": 0.41,
"total_gst": 1.60,
"status": "Created",
"tracking_summary": {
"Created": 1
},
"number_of_items": 1
},
"movement_type": "DESPATCH",
"charge_to_account": "1001746337"
}
]
}
What I don't understand is why Delphi is giving me this 404 error. If I don't include the body in the request when using Postman, it gives me this response:
{
"errors": [
{
"code": "400",
"name": "INVALID_PAYLOAD",
"message": "Unable to deserialize payload. Please ensure that the payload is valid JSON. Please note that when assigned, boolean fields can only accept 'true' or 'false' as values."
}
]
}
But if I don't include the body in the request from Delphi, I get the same 404 error. Does anyone know of a different REST API that I can try my code with? Or has anyone successfully used the AusPost eParcel API from Delphi, even if it was done with Indy?
Found the problem, even if I can't explain it. Once I removed the Content-Type parameter code, the post method worked fine.
// AParameter := RESTRequest.Params.AddItem;
// AParameter.ContentType := ctAPPLICATION_JSON;
// AParameter.name := 'Content-Type';
// AParameter.Value := 'application/json';
// AParameter.Kind := TRESTRequestParameterKind.pkHTTPHEADER;
Somehow, including that in the request just stuffs things up. No explanation as to why, but it may just be one of those Delphi REST library quirks.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.