Royal Mail Shipping API - conexão SOAP e consulta pem / certificados
Estou tentando configurar a API de envio do Royal Mail (se alguém tiver alguma experiência com isso, ficaria grato se você pudesse ajudar).
Na documentação fornecida, preciso baixar um certificado (um arquivo .p12) e importá-lo para minha máquina Windows - isso é bastante simples usando o 'Assistente para Importação de Certificados'. Quando chegar ao "Definir nível de segurança", devo selecionarAlto e isso solicitará permissão com uma senha sempre que for usada.
No Internet Explorer, em 'Opções da Internet', na guia Conteúdo, posso ver os Certificados e ver claramente que este certificado foi importado e não expirou.
O próximo passo éextrair os componentes do certificado, aqui eu tenho que executar os três comandos a seguir usando o OpenSSL para gerar os arquivos .pem.
$ 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
A documentação indica oO arquivo cacert.pem pode ser referenciado diretamente por um aplicativo usando o próprio arquivo, que acredito ter feito no meu script PHP, mas não está claro onde devo colocar o outroarquivos mycert e mykey pem.
A documentação declara o seguinte em relação a isso: -
Como um aplicativo passa o certificado SSL do cliente emitido ao estabelecer uma conexão de rede SSL depende do aplicativo e do ambiente, mas seria necessário acessar essencialmente o arquivo "mycert.pem" e "mykey.pem" ou, em alguns casos, um único arquivo combinado contendo certificado e chave.
Então, em nenhum lugar diz como esses dois arquivos são usados pelo aplicativo, no momento em que os deixei no mesmo diretório que o arquivo cacert.pem.
Se eu tentar acessar o URLhttps://api.royalmail.com/shipping/onboarding diretamente do navegador, ele solicita a seleção de um certificado, eu o seleciono e, em seguida, insiro a senha correta ao solicitar 'Conceder ou negar permissão para usar essa chave'. Depois de inserir a senha correta, a página a seguir é exibida - alguém pode confirmar se isso significa que o problema está no meu final OU algo que o Royal Mail não configurou corretamente no final.
Além disso, o script PHP real que tenho usado para enviar solicitações SOAP para a API de remessa não está funcionando (provavelmente relacionado a tudo acima).
No meu script PHP, as opções do soapclient são configuradas da seguinte maneira:
$soapclient_options['cache_wsdl'] = 'WSDL_CACHE_NONE';
$soapclient_options['local_cert'] = 'certs/cacert.pem';
$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';
$client = new SoapClient('SAPI/ShippingAPI_V2_0_8.wsdl', $soapclient_options);
$client->__setLocation($soapclient_options['location']);
Quando executo o script PHP (este é basicamente o mesmo código que o Royal Mail fornece aos meus próprios detalhes de login da API), recebo a seguinte mensagem no navegador:
Could not connect to host
REQUEST: [email protected] rngfJ+4dt4Gt855a5pr6u38i3B4= ODcwMTE5Nzc3 2015-10-13T11:02:20Z 2015-10-13T11:02:201.00526348001DeliveryDSD12015-10-13bobSS23, Some AvenueLondonE10g1000000
Obviamente, isso não pode se conectar ao host por algum motivo desconhecido, o último é simplesmente a solicitação que foi enviada. O restante do script PHP é exatamente o mesmo que o script do Royal Mail que eles enviaram para mim e confirmaram que é usado por outras pessoas e está funcionando bem.
Estou trabalhando em um ambiente WAMP, embora o código final esteja em um ambiente Linux.Alguém pode ajudar Estou realmente ficando perplexo, e o Royal Mail em si ainda não conseguiu fornecer nenhum suporte técnico sólido.
ATUALIZAREsta é a mensagem de erro completa exibida no navegador (alterei o endereço de email por motivos de segurança)
Invalid Request REQUEST: [email protected] dgCW98Vqw3ladYgPPpNialODhvI= MTMzMjE1NjM4 2015-10-13T13:25:30Z 2015-10-13T13:25:302.00526348001DeliveryDSD12015-10-13Jon DoeSS23, Some RoadLondonE10g1000000
Mesclei os dois arquivos pem em um único arquivo chamado 'bundle.pem' e referenciei isso na variável 'local_cert' para o SoapClient & BINGO que agora está sendo conectado. Isso agora mostra mais oNão foi possível conectar mas declara uma "Solicitação inválida". Portanto, pelo menos agora isso está se conectando e causando um erro diferente.
Meu script PHP inteiro está abaixo:
<?php
ini_set('default_socket_timeout', 120);
ini_set('soap.wsdl_cache_enabled',1);
ini_set('soap.wsdl_cache_ttl',1);
$api_password = "xxxxxxxxxxxxxx!";
$api_username = "[email protected]";
$api_application_id = "xxxxxxxxxxxx";
$api_service_type = "D";
$api_service_code = "SD1";
$api_service_format = "";
$api_certificate_passphrase = 'xxxxxxxxxx';
$api_service_enhancements = "";
$data = new ArrayObject();
$data->order_tracking_id = "";
$data->shipping_name = "Jon Doe";
$data->shipping_company = "SS";
$data->shipping_address1 = "23, Some Road";
$data->shipping_address2 = "";
$data->shipping_town = "London";
$data->shipping_postcode = "E1";
$data->order_tracking_boxes = "0";
$data->order_tracking_weight = "1000";
$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'] = 'royalmail/cert/bundle.pem';
$soapclient_options['passphrase'] = $api_certificate_passphrase;
$soapclient_options['trace'] = true;
$soapclient_options['ssl_method'] = 'SOAP_SSL_METHOD_SSLv3';
$soapclient_options['exceptions'] = true;
$soapclient_options['location'] = 'https://api.royalmail.com/shipping/onboarding';
//launch soap client
$client = new SoapClient('royalmail/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 );
$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:\n" . $client->__getLastRequest() . "\n";
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:\n" . $client->__getLastRequest() . "\n";
die;
?>
Apenas para maior clareza, adicionei um despejo da variável $ request pouco antes de atingir o bloco try / catch (observe que isso é meio longo).
Array
(
[integrationHeader] => Array
(
[dateTime] => 2015-10-13T13:34:44
[version] => 1.0
[identification] => Array
(
[applicationId] => 0526348001
[transactionId] =>
)
)
[requestedShipment] => Array
(
[shipmentType] => Array
(
[code] => Delivery
)
[serviceOccurence] => 1
[serviceType] => Array
(
[code] => D
)
[serviceOffering] => Array
(
[serviceOfferingCode] => Array
(
[code] => SD1
)
)
[serviceFormat] => Array
(
[serviceFormatCode] => Array
(
[code] =>
)
)
[shippingDate] => 2015-10-13
[recipientContact] => Array
(
[name] => Jon Doe
[complementaryName] => SS
)
[recipientAddress] => Array
(
[addressLine1] => 23, Some Road
[addressLine2] =>
[postTown] => London
[postcode] => E1
)
[items] => Array
(
[item] => Array
(
[numberOfItems] => 0
[weight] => Array
(
[unitOfMeasure] => Array
(
[unitOfMeasureCode] => Array
(
[code] => g
)
)
[value] => 1000000
)
)
)
)
)