I am setting up the initial Royal Mail Shipping API integration and have the following PHP code (p.s i'm still waiting for some credentials from Royal Mail but this code should still at least connect & give me some form of authentication error instead.
I have run PHPINFO() locally and can confirm both CURL & OpenSSL are enabled. Not quite sure what else could be causing this...any ideas?
I have turned off my Windows firewall and have the URL http://localhost/royalmail/index.php
When I run this in the browser I get the error - this happens within the try block that is around half way down in the block of code.
Could not connect to host
my code below
<?php
//phpinfo();
// AWAITING FROM ROYAL MAIL..
$api_password = "xxxx";
$api_username = "dispatch@company.com";
$api_application_id = "xxxxxx";
$api_service_type = "D";
$api_service_code = "SD1";
$api_service_format = "";
$api_certificate_passphrase = 'xxxxxxxxxxxx';
$api_service_enhancements = "";
// setting up the $data object
$data = new ArrayObject();
$data->order_tracking_id = "";
$data->shipping_name = "John";
$data->shipping_company = "AA";
$data->shipping_address1 = "23, Hurst Avenue";
$data->shipping_address2 = "";
$data->shipping_town = "London";
$data->shipping_postcode = "E1";
$data->order_tracking_boxes = "0";
$data->order_tracking_weight = "1500";
$time = gmdate('Y-m-d\TH:i:s');
$created = gmdate('Y-m-d\TH:i:s\Z');
$nonce = mt_rand();
$nonce_date_pwd = pack("A*",$nonce) . pack("A*",$created) . pack("H*", sha1($api_password));
$passwordDigest = base64_encode(pack('H*',sha1($nonce_date_pwd)));
$ENCODEDNONCE = base64_encode($nonce);
$soapclient_options = array();
$soapclient_options['cache_wsdl'] = 'WSDL_CACHE_NONE';
$soapclient_options['local_cert'] = 'certs/CA2+Company+John-Bloggs+RM10001790+usr.p12';
$soapclient_options['passphrase'] = $api_certificate_passphrase;
$soapclient_options['trace'] = true;
$soapclient_options['ssl_method'] = 'SOAP_SSL_METHOD_SSLv3';
$soapclient_options['location'] = 'https://api.royalmail.com/shipping/onboarding';
//launch soap client
$client = new SoapClient('SAPI/ShippingAPI_V2_0_8.wsdl', $soapclient_options);
$client->__setLocation($soapclient_options['location']);
//headers needed for royal mail
$HeaderObjectXML = '<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-000">
<wsse:Username>'.$api_username.'</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">'.$passwordDigest.'</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">'.$ENCODEDNONCE.'</wsse:Nonce>
<wsu:Created>'.$created.'</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>';
//push the header into soap
$HeaderObject = new SoapVar( $HeaderObjectXML, XSD_ANYXML );
//push soap header
$header = new SoapHeader( 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd', 'Security', $HeaderObject );
//$header = new SoapHeader( 'SAPI/oasis-200401-wss-wssecurity-utility-1.0.xsd', 'Security', $HeaderObject );
$client->__setSoapHeaders($header);
//build the request
$request = array(
'integrationHeader' => array(
'dateTime' => $time,
'version' => '1.0',
'identification' => array(
'applicationId' => $api_application_id,
'transactionId' => $data->order_tracking_id
)
),
'requestedShipment' => array(
'shipmentType' => array('code' => 'Delivery'),
'serviceOccurence' => '1',
'serviceType' => array('code' => $api_service_type),
'serviceOffering' => array('serviceOfferingCode' => array('code' => $api_service_code)),
'serviceFormat' => array('serviceFormatCode' => array('code' => $api_service_format)),
'shippingDate' => date('Y-m-d'),
'recipientContact' => array('name' => $data->shipping_name, 'complementaryName' => $data->shipping_company),
'recipientAddress' => array('addressLine1' => $data->shipping_address1, 'addressLine2' => $data->shipping_address2, 'postTown' => $data->shipping_town, 'postcode' => $data->shipping_postcode),
'items' => array('item' => array(
'numberOfItems' => $data->order_tracking_boxes,
'weight' => array( 'unitOfMeasure' => array('unitOfMeasureCode' => array('code' => 'g')), 'value' => ($data->order_tracking_weight*1000) //weight of each individual item
)
)
)
)
);
//if any enhancements, add it into the array
if($api_service_enhancements != "") {
$request['requestedShipment']['serviceEnhancements'] = array('enhancementType' => array('serviceEnhancementCode' => array('code' => $api_service_enhancements)));
}
//try make the call
try {
$response = $client->__soapCall( 'createShipment', array($request), array('soapaction' => 'https://api.royalmail.com/shipping/onboarding') );
} catch (Exception $e) {
//catch the error message and echo the last request for debug
echo $e->getMessage();
echo "REQUEST:
" . $client->__getLastRequest() . "
";
die;
}
//check for any errors
if(isset($response->integrationFooter->errors)) {
$build = "";
//check it wasn't a single error message
if(isset($response->integrationFooter->errors->error->errorCode)) {
$build .= $output_error->errorCode.": ".$output_error->errorDescription."<br/>";
} else {
//loop out each error message, throw exception will be added ehre
foreach($response->integrationFooter->errors->error as $output_error) {
$build .= $output_error->errorCode.": ".$output_error->errorDescription."<br/>";
}
}
echo $build; die;
}
print_r($response);
echo "REQUEST:
" . $client->__getLastRequest() . "
";
die;
?>
Where the error occurs..
try {
$response = $client->__soapCall( 'createShipment', array($request), array('soapaction' => 'https://api.royalmail.com/shipping/onboarding') );
} catch (Exception $e) {
//catch the error message and echo the last request for debug
echo $e->getMessage();
echo "REQUEST:
" . $client->__getLastRequest() . "
";
die;
}
update
I haven't created the *.pem files for this - I read this in the guide. I have created the three *.pem files (cacert.pem, mycert.pem & mykey.pem) but i'm not sure what to do with them now?
The PKCS#12 [http://en.wikipedia.org/wiki/PKCS_12] is the original industry standard from 1996 for the secure transfer of certificates and private keys (similar to an encrypted zip archive) and is directly supported by a variety of applications and Operating Systems. This provides a standardized 'single file' way to securely pass a certificate, its private key and a copy of the CA's public signing certificate.
The standard web server on Linux and BSD is Apache and that expects that all certificates and keys to be in a Base64 encoded version of the CER/DER x509v3 binary certificate format and RSA/DSA keys.
Extracting the constituent parts from a PKCS#12 file (mycert.p12) is easy on Linux using the following OpenSSL toolset commands ..
% openssl pkcs12 -in mycert.p12 -cacerts -nokeys -out cacert.pem
% openssl pkcs12 -in mycert.p12 -clcerts -nokeys -out mycert.pem
% openssl pkcs12 -in mycert.p12 -nocerts -nodes -out mykey.pem
The "cacert.pem" file contains CA's public signing. Typically it is added to a system-wide "Trusted CA" file (e.g. /etc/ssl/certs/ca-certificates.crt etc) or can be directly referenced by an application using the file itself. This certificate is needed to formally validate any certificates issued by the same CA.
Typically the "mycert.pem" file contains the PEM encoded x509v3 format "Client SSL" certificate. This is used to authenticate the client to a server when establishing an SSL/TLS connection.
The "mykey.pem" file contains the (now) unencrypted PEM encoded RSA 2048-bit private key linked to the client certificate.
How an application passes the issued client SSL certificate when establishing an SSL network connection to is application and environment dependent but it would essentially need to access both the "mycert.pem" and "mykey.pem" file, or in some cases, a single combined file containing both cert and key.