Royal Mail Shipping API - SOAP connection & pem/certificates query

故事扮演 提交于 2019-11-29 08:59:04

Firstly, accessing the https://api.royalmail.com/shipping/onboarding directly will not work, because it's only accessible via the API.

With Royal Mail, do you have all of the CDM files and WSDL files? Make sure the CDM files are in the same directory as the WSDL files.

Here's what I did when working with the API;

$client = new SoapClient("/royalmail/ShippingAPI_V2_0_8.wsdl", array(
                                                    'trace' => 1,
                                                    'location'   => $location, //https://api.royalmail.com/shipping
                                                    'soap_version' => SOAP_1_1,
                                                    'local_cert' => '/royalmail/cert/cert.pem',
                                                    'passphrase' => 'xxx',
                                                    'exceptions' => true
                                                ));

Then, when it came to actually making the connection, I did something like this:

    $password = 'xxx';

    $date = gmdate('Y-m-d\TH:i:s\Z');
    $nonce = mt_rand();
    $nonce_date_pwd = pack("A*",$nonce) . pack("A*",$date) . pack("H*", sha1($password));
    $encoded_password = base64_encode(pack('H*',sha1($nonce_date_pwd)));
    $encoded_nonce = base64_encode($nonce);

    $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-0000">
                      <wsse:Username>Username</wsse:Username>
                      <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">' . $encoded_password . '</wsse:Password>
                      <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">' . $encoded_nonce . '</wsse:Nonce>
                      <wsu:Created>'.$date.'</wsu:Created>
                   </wsse:UsernameToken>
               </wsse:Security>';

    $HeaderObject = new SoapVar( $HeaderObjectXML, XSD_ANYXML );

    $header = new SoapHeader( 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd', 'Security', $HeaderObject );
    $client->__setSoapHeaders( $header );

    $request = array('the shipment request');

    try {
        $client->__soapCall( 'createShipment', array($request) );
    }
    catch (SoapFault $soapFault) {
       print_r($soapFault);
    }

I hope this helps.

Edit

Try this request

I think you need to use Version 2 for that Service Occurrence, if I remember rightly. Also, just...formatting. Makes it easier to debug.

$request = Array(

    'integrationHeader' => array(
        'dateTime' => date('Y-m-d\TH:i:s'),
        'version' => '2',
        'identification' => array(
            'applicationId' => $api_application_id,
            'transactionId' => $data->order_tracking_id
        ),

    ),

    'requestedShipment' => array(   
        'shipmentType' => array(
            'code' => 'Delivery'
        ),
    'serviceOccurrence' => 1,
        'serviceType' => array(
            'code' => $api_service_type
        ),
        'serviceOffering' => array(
            'serviceOfferingCode' => array(
                'code' => $api_service_code
            )
        ),
        'serviceFormat' => array(
            'serviceFormatCode' => array(
                'code' => $api_service_format
            )
        ),
        'shippingDate' => gmdate('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)
                )
            )
        )

    )
);

! Although you've mentioned you've done most of these steps, I'll detail them anyway to complete the instructions

Installing the CA to the server

  • Upload the p12 file to the server (in /root/Desktop for example)
  • Run the following 3 commands to install
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 
  • Now copy the *.pem files into /etc/ssl/certs
    • I like to create a subdirectory in here (mkdir royalmail)
    • And move the *.pem files (mv *.pem /etc/ssl/certs/certificates)
  • Now create a new file and copy the contents of mycert.pem and mykey.pem into it (only from -----BEGIN .... ------- to EOF)

You should have the following files;

Using SOAPClient

Now that the certificates are installed, we can now test the connection (assuming your certificates are in /etc/pki/tls/certs/certificates/royalmail/shippingv2)

If you run

wget https://api.royalmail.com/shipping/onboarding --private-key=/etc/ssl/certs/certificates/royalmail/shippingv2/rm_bundle.pem --private-key-type=PEM

You should be able to connect to port 443 (there may be a handshake failure in OpenSSL though - at least with my test just now on our staging environment).

We can now instantiate the SoapClient using a local copy of the WSDL and specifying the local certificate in the $options parameter.

$objSoapClient = new \SoapClient('lib/wsdl/royalmail/shipping/ShippingAPI_V2_0_8.wsdl', array(
    'soap_version' => SOAP_1_1,
    'trace' => 1,
    'uri' => 'http://www.royalmailgroup.com/api/ship/V2',
    'location' => 'https://api.royalmail.com/shipping/onboarding',
    'local_cert' => '/etc/ssl/certs/certificates/royalmail/shippingv2/rm_bundle.pem',
    'passphrase' => '', //Your passphrase when doing step 1
    'ssl_method' => 'SOAP_SSL_METHOD_TLS',
    'exceptions' => 1,
    'trace' => 1
));

Authentication

In your documentation, you should find a file called rm_password_digest.php or something similar, which details how to create the authentication headers.

/* The value below should be changed to your password.  If you store the password  */  
/* as hashed in your database, you will need to change the code below to remove hashing */

$password = 'just_my_royalmail_api_password';

/* CREATIONDATE - The timestamp. The computer must be on correct time or the server you are
* connecting may reject the password digest for security.
*/
$CREATIONDATE = gmdate('Y-m-d\TH:i:s\Z');

/* NONCE - A random word. The use of rand() may repeat the word if the server is
* very loaded.
*/
$nonce = mt_rand();

/* PASSWORDDIGEST This is the way to create the password digest. As per OASIS standard
*  digest = base64_encode(Sha1(nonce + creationdate + password)
*  however note that we use a SHA1(password) instead of the password above
*/
$nonce_date_pwd = pack("A*",$nonce) . pack("A*",$CREATIONDATE) . pack("H*", sha1($password));
$PASSWORDDIGEST = base64_encode(
pack('H*', sha1($nonce_date_pwd)));

/* ENCODEDNONCE - Now encode the nonce for security header */

$ENCODEDNONCE = base64_encode($nonce);

/* Now Print all the values - so we can use it for testing with tools like soapui */

print "WS Security Header elements \n";
print "--------------------------- \n";
print 'Nonce = ' . $nonce;
print "\n";
print 'PASSWORDDIGEST= ' . $PASSWORDDIGEST;
print "\n";
print 'ENCODEDNONCE= ' . $ENCODEDNONCE;
print "\n";
print "CREATIONDATE= " . $CREATIONDATE;

This will help you build the following in the SOAPHeader

<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-0000">
        <wsse:Username>[...]</wsse:Username>
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">[...]</wsse:Password>
        <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">[...]</wsse:Nonce>
        <wsu:Created>[...]</wsu:Created>
    </wsse:UsernameToken>
</wsse:Security>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!