cybersource php ruby-on-rails ruby soap savon

php - cybersource api



Cómo formatear Savon Request for Cybersource SOAP API (1)

Tratando de averiguar cómo formatear una solicitud de pagos de Cybersource, utilizando Savon y Ruby.

He estado en esto por un tiempo, sin suerte. Sigo recibiendo RequestMessage no compatible

Supongo que se debe a cómo paso los parámetros al cuerpo del mensaje y / o al encabezado no configurado correctamente.

Aquí está el xml esperado para la API de SOAP:

<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Header> <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1"> <wsse:UsernameToken> <wsse:Username>yourMerchantID</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">yourPassword</wsse:Password> </wsse:UsernameToken> </wsse:Security> </soapenv:Header> <soapenv:Body> <requestMessage xmlns="urn:schemas-cybersource-com:transaction-data-N.NN"> <merchantID>yourMerchantID</merchantID> <merchantReferenceCode>MRC-123</merchantReferenceCode> <billTo> <firstName>John</firstName> <lastName>Doe</lastName> <street1>1295 Charleston Road</street1> <city>Mountain View</city> <state>CA</state> <postalCode>94043</postalCode> <country>US</country> <email>[email protected]</email> </billTo> <item id="0"> <unitPrice>5.00</unitPrice> <quantity>1</quantity> </item> <item id="1"> <unitPrice>10.00</unitPrice> <quantity>2</quantity> </item> <purchaseTotals> <currency>USD</currency> </purchaseTotals> <card> <accountNumber>4111111111111111</accountNumber> <expirationMonth>11</expirationMonth> <expirationYear>2020</expirationYear> </card> <ccAuthService run="true" /> </requestMessage> </soapenv:Body> </soapenv:Envelope>

Este es el xml que obtengo cuando intento hacer una solicitud.

<?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:data="urn:schemas-cybersource-com:transaction-data:TransactionProcessor" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"> <env:Header> <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-1"> <wsse:Username>GiveCampusCDW</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">ju12trzJpnO81ZwSxPdy5htTVeOyUmICDWNmWjXuimTx9Qy+myOB4B4G8ItiJdfu37pJ6jJO2OAmCDIAoWjlgeMO5mvlYxKkVAoDEi2b2dxwLzJlkjUhhyznNzbz71b96lFRgoHGO2YpSlmT5VzTATNVt6SBUVV+iG3D3nndMwAPOmw5M+jSwP0xubZGYPV9bvuCFXI/GcNTsQYN9DWinqMjmq5zw13VgSObQFTPTn5iR+wGcOaj+1fK7IJjYlz82uRF0RHK7JTt0UIDsxULarEiJZBs+VFq9LjPblWI28365bHFs7ooNrgYJkVz+byCaswTj1wWeUecOX3L452zsQ==</wsse:Password> </wsse:UsernameToken> </wsse:Security> </env:Header> <env:Body> <data:requestMessage xmlns="urn:schemas-cybersource-com:transaction-data-1.129"> <merchantID>GiveCampusCDW</merchantID> <merchantReferenceCode>ContributionID</merchantReferenceCode> <billTo> <firstName>Saul</firstName> <lastName>Goodman</lastName> <street1>1295 Charleston Road</street1> <city>Mountain View</city> <state>CA</state> <postalCode>94043</postalCode> <country>US</country> <email>[email protected]</email> </billTo> <item> <unitPrice>50.00</unitPrice> <quantity>1</quantity> </item> <purchaseTotals> <currency>USD</currency> </purchaseTotals> <card> <accountNumber>4111111111111111</accountNumber> <expirationMonth>12</expirationMonth> <expirationYear>2020</expirationYear> </card> <ccAuthService> <run>true</run> </ccAuthService> </data:requestMessage> </env:Body> </env:Envelope>

Este es el error que se devuelve:

ybersource::SoapException ((soap:Client) Element (urn:schemas-cybersource-com:transaction-data:TransactionProcessor):requestMessage not supported. ): lib/cybersource/client.rb:73:in `rescue in run_transaction'' lib/cybersource/client.rb:38:in `run_transaction'' app/controllers/transactions_controller.rb:7:in `new''

Aquí está mi clase de ruby, usada para envolver todo y hacer la llamada.

module Cybersource class Client attr_reader :merchant_id, :transaction_key def initialize(merchant_id, transaction_key) @merchant_id = merchant_id @transaction_key = transaction_key end def client # set the header which includes the merchant_id and transaction_key soap_header = <<-HEREDOC <SOAP-ENV:Header xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <wsse:Security SOAP-ENV:mustUnderstand="1"> <wsse:UsernameToken> <wsse:Username>#{@merchant_id}</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">#{@transaction_key}</wsse:Password> </wsse:UsernameToken> </wsse:Security> </SOAP-ENV:Header> HEREDOC # initialize a Savon client Savon.client( env_namespace: ''soapenv'', #namespace: "urn:schemas-cybersource-com:transaction-data:TransactionProcessor", soap_header: soap_header, #endpoint: "http://ics2wstest.ic3.com", wsdl: "https://ics2wstest.ic3.com/commerce/1.x/transactionProcessor/CyberSourceTransaction_1.129.wsdl", pretty_print_xml: true, logger: Rails.logger, log: true ) end def run_transaction # build up the xml message passed to the web service message = { merchantID: @merchant_id, merchantReferenceCode: rand(100), billTo: { firstName: "Saul", lastName: "Goodman", street1: "1295 Charleston Road", city: "Mountain View", state: "CA", postalCode: "94043", country: "US", email: "[email protected]", }, item: { unitPrice: "50.00", quantity: "1", }, purchaseTotals: { currency: "USD" }, card: { accountNumber: "4111111111111111", expirationMonth: "12", expirationYear: "2020" }, ccAuthService: {run: "true"}, } response = client.call(:run_transaction, message: message, :attributes => { # sets the xmlns on the requestMessage tag ''xmlns'' => ''urn:schemas-cybersource-com:transaction-data-1.129'', }) # return the response body response.body[:response] rescue Savon::SOAPFault => error raise Cybersource::SoapException, error end protected def wsdl_url if Rails.env.production? ENV["CYBERSOURCE_LIVE_WSDL_URL"] else ENV["CYBERSOURCE_TEST_WSDL_URL"] end end end end

Solo tengo un ejemplo de código PHP para irme, pero no estoy seguro de cómo convertirlo en ruby.

<HTML> <HEAD> <META HTTP-EQUIV="Content-Type" content="text/html; charset=iso-8859-1"> <TITLE>Order Status</TITLE> </HEAD> <BODY> <?php // Before using this example, replace the generic values with your merchant ID and password. define( ''MERCHANT_ID'', ''your_merchant_id'' ); define( ''TRANSACTION_KEY'', ''your_transaction_key'' ); define( ''WSDL_URL'', ''https://ics2wstest.ic3.com/commerce/1.x/transactionProcessor/CyberSourceTransaction_1.26.wsdl'' ); class ExtendedClient extends SoapClient { function __construct($wsdl, $options = null) { parent::__construct($wsdl, $options); } // This section inserts the UsernameToken information in the outgoing SOAP message. function __doRequest($request, $location, $action, $version) { $user = MERCHANT_ID; $password = TRANSACTION_KEY; $soapHeader = "<SOAP-ENV:Header xmlns:SOAP-ENV=/"http://schemas.xmlsoap.org/soap/envelope//" xmlns:wsse=/"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd/"><wsse:Security SOAP-ENV:mustUnderstand=/"1/"><wsse:UsernameToken><wsse:Username>$user</wsse:Username><wsse:Password Type=/"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText/">$password</wsse:Password></wsse:UsernameToken></wsse:Security></SOAP-ENV:Header>"; $requestDOM = new DOMDocument(''1.0''); $soapHeaderDOM = new DOMDocument(''1.0''); try { $requestDOM->loadXML($request); $soapHeaderDOM->loadXML($soapHeader); $node = $requestDOM->importNode($soapHeaderDOM->firstChild, true); $requestDOM->firstChild->insertBefore( $node, $requestDOM->firstChild->firstChild); $request = $requestDOM->saveXML(); // printf( "Modified Request:/n*$request*/n" ); } catch (DOMException $e) { die( ''Error adding UsernameToken: '' . $e->code); } return parent::__doRequest($request, $location, $action, $version); } } try { $soapClient = new ExtendedClient(WSDL_URL, array()); /* To see the functions and types that the SOAP extension can automatically generate from the WSDL file, uncomment this section: $functions = $soapClient->__getFunctions(); print_r($functions); $types = $soapClient->__getTypes(); print_r($types); */ $request = new stdClass(); $request->merchantID = MERCHANT_ID; // Before using this example, replace the generic value with your own. $request->merchantReferenceCode = "your_merchant_reference_code"; // To help us troubleshoot any problems that you may encounter, // please include the following information about your PHP application. $request->clientLibrary = "PHP"; $request->clientLibraryVersion = phpversion(); $request->clientEnvironment = php_uname(); // This section contains a sample transaction request for the authorization // service with complete billing, payment card, and purchase (two items) information. $ccAuthService = new stdClass(); $ccAuthService->run = "true"; $request->ccAuthService = $ccAuthService; $billTo = new stdClass(); $billTo->firstName = "John"; $billTo->lastName = "Doe"; $billTo->street1 = "1295 Charleston Road"; $billTo->city = "Mountain View"; $billTo->state = "CA"; $billTo->postalCode = "94043"; $billTo->country = "US"; $billTo->email = "[email protected]"; $billTo->ipAddress = "10.7.111.111"; $request->billTo = $billTo; $card = new stdClass(); $card->accountNumber = "4111111111111111"; $card->expirationMonth = "12"; $card->expirationYear = "2020"; $request->card = $card; $purchaseTotals = new stdClass(); $purchaseTotals->currency = "USD"; $request->purchaseTotals = $purchaseTotals; $item0 = new stdClass(); $item0->unitPrice = "12.34"; $item0->quantity = "2"; $item0->id = "0"; $item1 = new stdClass(); $item1->unitPrice = "56.78"; $item1->id = "1"; $request->item = array($item0, $item1); $reply = $soapClient->runTransaction($request); // This section will show all the reply fields. // var_dump($reply); // To retrieve individual reply fields, follow these examples. printf( "decision = $reply->decision<br>" ); printf( "reasonCode = $reply->reasonCode<br>" ); printf( "requestID = $reply->requestID<br>" ); printf( "requestToken = $reply->requestToken<br>" ); printf( "ccAuthReply->reasonCode = " . $reply->ccAuthReply->reasonCode . "<br>"); } catch (SoapFault $exception) { var_dump(get_class($exception)); var_dump($exception); } ?> </BODY> </HTML>

Cualquier ayuda sería muy apreciada.


Primero permítanme decir que mi conocimiento de Ruby es mínimo, así que no puedo ayudar con el cliente de Savon. Pero probé la solicitud XML sin requestMessage y me di cuenta de que precisamente requestMessage terminó en el espacio de nombres incorrecto:

<data:requestMessage>

se refiere a xmlns:data="urn:schemas-cybersource-com:transaction-data:TransactionProcessor"

cuando debería ser xmlns:data="urn:schemas-cybersource-com:transaction-data-1.129"

Veo que ha comentado el parámetro del espacio de nombres en la inicialización de su cliente. Esa puede ser la forma de establecer el espacio de nombres en caso de que el cliente no lo lea desde el wsdl.

Según esta respuesta , puede especificar diferentes espacios de nombres según sea necesario.

Actualizar

Ok, creo que lo hice funcionar. Echa un vistazo a este script:

require ''savon'' soap_header = <<-HEREDOC <wsse:Security> <wsse:UsernameToken> <wsse:Username>username</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">transaction_key</wsse:Password> </wsse:UsernameToken> </wsse:Security> HEREDOC client = Savon.client( wsdl: ''https://ics2wstest.ic3.com/commerce/1.x/transactionProcessor/CyberSourceTransaction_1.129.wsdl'', soap_header: soap_header, env_namespace: ''soapenv'', element_form_default: :unqualified, namespace: "urn:schemas-cybersource-com:transaction-data-1.129", namespaces: { "xmlns:wsse": "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" }, pretty_print_xml: true, #logger: Rails.logger, log: true ) message = { merchantID: "merch_id", merchantReferenceCode: rand(100), billTo: { firstName: "Saul", lastName: "Goodman", street1: "1295 Charleston Road", city: "Mountain View", state: "CA", postalCode: "94043", country: "US", email: "[email protected]", }, item: { unitPrice: "50.00", quantity: "1", }, purchaseTotals: { currency: "USD" }, card: { accountNumber: "4111111111111111", expirationMonth: "12", expirationYear: "2020" }, ccAuthService: { :@run => "true" } } response = client.call(:run_transaction, message: message, :attributes => { ''xmlns'' => ''urn:schemas-cybersource-com:transaction-data-1.129'', }) response.body[:response]

Con esto estoy obteniendo la autenticación fallida como se esperaba. Entonces, un par de cosas que estaban mal:

  • La definición del encabezado debe omitir el nodo Header , simplemente comience con los nodos internos ( Security )

  • El run => true en ccAuthService es un atributo, no un nodo interno.

  • Todo el desorden de requestMessage nombres de requestMessage .