jjzjj

php - 如何为 Cyber​​source SOAP API 格式化 Savon Request

coder 2023-06-14 原文

尝试找出如何使用 Savon 和 Ruby 格式化 Cyber​​source 付款请求。

我已经做了一段时间了,但运气不好。我不断得到 不支持请求消息

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

这是 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>

如有任何帮助,我们将不胜感激。

最佳答案

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

<data:requestMessage>

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]

有了这个,我得到了预期的身份验证失败。 所以,有几件事是错误的:

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

  • run => trueccAuthService是属性,不是内部节点。

  • requestMessage 的整个命名空间困惑.

关于php - 如何为 Cyber​​source SOAP API 格式化 Savon Request,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38708526/

有关php - 如何为 Cyber​​source SOAP API 格式化 Savon Request的更多相关文章

  1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  2. ruby - 如何为 emacs 安装 ruby​​-mode - 2

    我刚刚为fedora安装了emacs。我想用emacs编写ruby。为ruby​​提供代码提示、代码完成类型功能所需的工具、扩展是什么? 最佳答案 ruby-mode已经包含在Emacs23之后的版本中。不过,它也可以通过ELPA获得。您可能感兴趣的其他一些事情是集成RVM、feature-mode(Cucumber)、rspec-mode、ruby-electric、inf-ruby、rinari(用于Rails)等。这是我当前用于Ruby开发的Emacs配置:https://github.com/citizen428/emacs

  3. ruby-on-rails - 将 Ruby 中的日期/时间格式化为 YYYY-MM-DD HH :MM:SS - 2

    这个问题在这里已经有了答案:Railsformattingdate(4个答案)关闭4年前。我想格式化Time.Now函数以显示YYYY-MM-DDHH:MM:SS而不是:“2018-03-0909:47:19+0000”该函数需要放在时间中.现在功能。require‘roo’require‘roo-xls’require‘byebug’file_name=ARGV.first||“Template.xlsx”excel_file=Roo::Spreadsheet.open(“./#{file_name}“,extension::xlsx)xml=Nokogiri::XML::Build

  4. ruby - 我可以将我的 README.textile 以正确的格式放入我的 RDoc 中吗? - 2

    我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:

  5. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  6. ruby-on-rails - 事件管理员日期过滤器日期格式自定义 - 2

    是否有简单的方法来更改默认ISO格式(yyyy-mm-dd)的ActiveAdmin日期过滤器显示格式? 最佳答案 您可以像这样为日期选择器提供额外的选项,而不是覆盖js:=f.input:my_date,as::datepicker,datepicker_options:{dateFormat:"mm/dd/yy"} 关于ruby-on-rails-事件管理员日期过滤器日期格式自定义,我们在StackOverflow上找到一个类似的问题: https://s

  7. ruby-on-rails - 如何在 Rails 中设置路由的默认格式? - 2

    路由有如下代码:resources:orders,only:[:create],defaults:{format:'json'}resources:users,only:[:create,:update],defaults:{format:'json'}resources:delivery_types,only:[:index],defaults:{format:'json'}resources:time_corrections,only:[:index],defaults:{format:'json'}是否可以使用1个字符串为所有资源设置默认格式,每行不带“默认值”散列?谢谢。

  8. ruby-on-rails - Rails 4 WYSIWYG Bootsy 不显示格式 - 2

    我刚刚按照thebootsygempage上的安装说明进行操作在我保存并查看帖子内容之前,一切看起来都不错。这是输出在View中的样子:HeaderSubhead:似乎没有呈现任何html格式,因为它被引号或类似的东西转义了-其他人有这个问题吗?我没有在github页面或SO上看到任何问题来指出我正确的方向。除了遵循gem安装说明之外,我还没有做任何事情,但也许我错过了什么或者只是犯了一个愚蠢的错误。如果你还有什么想知道的,请尽管问。干杯 最佳答案 你需要有这样的东西,转义html: 关

  9. ruby - 在 Ruby 中将整数格式化为固定长度的字符串 - 2

    有没有一种简单的方法可以将给定的整数格式化为具有固定长度和前导零的字符串?#convertnumberstostringsoffixedlength3[1,12,123,1234].map{|e|???}=>["001","012","123","234"]我找到了解决方案,但也许还有更聪明的方法。format('%03d',e)[-3..-1] 最佳答案 如何使用%1000而不是进行字符串操作来获取最后三位数字?[1,12,123,1234].map{|e|format('%03d',e%1000)}更新:根据theTinMan的

  10. ruby-on-rails - 如何正确格式化字符串,如 'mccdougal' 到 'McDougal' - 2

    什么Ruby或RailsDSL会将字符串"mccdougal"格式化为"McDougal",同时留下字符串"McDougal"原样?将titleize传递给"McDougal"结果如下:"McDougal".titleize#=>"McDougal" 最佳答案 据我所知,没有可以处理这种情况的Rails助手。这是一个非标准的边缘案例,需要特殊处理。但是,您可以创建自定义字符串变形。您可以将这段代码放入初始化程序中:ActiveSupport::Inflector.inflections(:en)do|inflect|inflect.

随机推荐