【问题标题】:How to format Savon Request for Cybersource SOAP API如何格式化 Cyber​​source SOAP API 的 Savon 请求
【发布时间】:2016-08-01 21:37:12
【问题描述】:

试图弄清楚如何使用 Savon 和 Ruby 格式化 Cyber​​source 付款请求。

我已经有一段时间了,没有运气。我不断得到 不支持 requestMessage

我猜这是因为我将参数传递给消息正文的方式,和/或标头设置不正确。

这是 SOAP API 的预期 xml:

<?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>null@cybersource.com</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>   

这是我尝试发出请求时得到的 xml。

 <?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>test@example.com</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>

这是返回的错误:

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'

这是我的 ruby​​ 类,用于将这一切包装起来并进行调用。

    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: "test@example.com",
          },
          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

我只有一个 PHP 代码示例可供使用,但我不确定如何将其转换为 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 = "null@cybersource.com";
    $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>

任何帮助将不胜感激。

【问题讨论】:

    标签: php ruby-on-rails ruby soap savon


    【解决方案1】:

    首先让我说我对 Ruby 的了解很少,所以我无法帮助 Savon 客户端。但我确实尝试了您的原始 xml 请求,并意识到 requestMessage 最终出现在错误的命名空间中:

    &lt;data:requestMessage&gt;

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

    什么时候应该是xmlns:data="urn:schemas-cybersource-com:transaction-data-1.129"

    我看到您在客户端初始化中注释掉了命名空间参数。这可能是设置命名空间的方式,以防客户端不从 wsdl 读取它。

    根据this answer,您可以根据需要指定不同的命名空间。


    更新

    好的,我认为我让它工作了。看看这个脚本:

    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: "test@example.com",
        },
        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]
    

    有了这个,我得到了 Authentication failed ,如预期的那样。 所以,有几件事是错误的:

    • 标头定义应省略Header 节点,只需从内部节点开始(Security

    • ccAuthService 中的run =&gt; true 是一个属性,而不是内部节点。

    • requestMessage 的整个命名空间混乱。

    【讨论】:

    • 根据您的评论,我已经更新了代码。我有一个旧版本的问题......但结果仍然是一样的。抛出相同的错误。还有其他想法吗?
    • 但是你看,它仍然有data:前缀。
    • 如果在Savon.client(...中设置命名空间会怎样
    • 这将在信封上设置命名空间,而不是 requestMessage。如果我在 Savon.client 中添加命名空间,它会返回和 XMLParse 错误。
    • 感谢您的努力......但我大约一个小时前就开始工作了。我将在几篇文章中发布我的答案。你已经很接近了……但我最终还需要做其他事情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-11
    • 1970-01-01
    • 2016-07-24
    • 1970-01-01
    • 2012-12-02
    • 1970-01-01
    • 2023-04-03
    相关资源
    最近更新 更多