I'm using ActiveMerchant to integrate with Authorize.net CIM. I'm in the middle of writing up automated tests, and I've begun putting into place Webmock calls so that my tests aren't actually hitting Authorize.net each time that they run.
I've created XML files from the responses of raw request data, and for the most part, it's working well. However, when I mock up a successful response, ActiveMerchant for some reason still tells me that Response.success? is not true.
My function
if self.cim_customer_profile_id.nil?
ActiveMerchant::Billing::Base.mode = :test
customer_profile_information = {
:profile => {
:merchant_customer_id => self.customer.username.first(20),
:email => self.customer.email
}
}
gateway = ActiveMerchant::Billing::AuthorizeNetCimGateway.new(
:login => AUTHORIZE_NET_API_LOGIN_ID,
:password => AUTHORIZE_NET_API_TRANSACTION_KEY
)
response = gateway.create_customer_profile(customer_profile_information)
if response.success?
self.cim_customer_profile_id = response.authorization
else
raise StandardError, response.message
end
end
And then my stubbed response is:
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
<?xml version="1.0" encoding="utf-8"?>
<createCustomerProfileResponse xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns='AnetApi/xml/v1/schema/AnetApiSchema.xsd'>
<messages>
<resultCode>
Ok
</resultCode>
<message>
<code>
I00001
</code>
<text>
Successful.
</text>
</message>
</messages>
<customerProfileId>10793616</customerProfileId>
<customerPaymentProfileIdList/>
<customerShippingAddressIdList/>
<validationDirectResponseList/>
</createCustomerProfileResponse>
Is there any reason why ActiveMerchant won't function with a successful, stubbed, request? Or am I missing something that ActiveMerchant requires in order to register that the response is actually successful?
Ah, I'm so dumb. I added new lines after all of my XML tags for readability, but they're interfering with how ActiveMerchant parses and evaluates the response.
So the correct XML response mock would be:
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
<?xml version="1.0" encoding="utf-8"?>
<createCustomerProfileResponse xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns='AnetApi/xml/v1/schema/AnetApiSchema.xsd'>
<messages>
<resultCode>Ok</resultCode>
<message>
<code>I00001</code>
<text>Successful.</text>
</message>
</messages>
<customerProfileId>10793616</customerProfileId>
<customerPaymentProfileIdList/>
<customerShippingAddressIdList/>
<validationDirectResponseList/>
</createCustomerProfileResponse>
Related
I'm using savon to make requests against a SOAP service.
Regardless, I'm having trouble making Savon client call.
below mentioned are my ruby code
client = Savon.client(
wsdl: "https://<URL>",
soap_header: { 'Content-Type:' => "text/xml"},
log: true,
pretty_print_xml: true
)
client.call(:get_active_employees_info,:xmlns =>"http://tempuri.org/") do
message("AppID" => "*******","Username" => "*****","Password" => "******")
end
which return
Savon::HTTPError: HTTP error (400)
but same snippets run on Postman
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<GetActiveEmployeesInfo xmlns="http://tempuri.org/">
<AppID>**********</AppID>
<Username>********</Username>
<Password>**********</Password>
</GetActiveEmployeesInfo>
</soap12:Body>
</soap12:Envelope>
I think that the problem is related to the request body that you are building.
Since you have a working example, I would start by checking the request's body build by savon. To do this, use the following code:
ops = client.operation(:get_active_employees_info)
puts ops.build(message: {"AppID" => "*******","Username" => "*****","Password" => "******"}).pretty
Then, compare generated message with the one from Postman. I hope it helps.
I need to remove the SOAPAction from my request using Savon 2.
This is how I build the client:
client = Savon.client do
wsdl "http://servername:port/PingService?wsdl"
convert_request_keys_to :none
env_namespace :soapenv
namespaces({
'xmlns:pin' => 'http://servername:port/pingService_v1'
})
end
I use the following call to make the request:
client.call(:invoke, message: { "pin:PingReq" => { "pin:PingDB" => "true", "pin:PingG" => "true" }})
This is the request that is sent:
<soapenv:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://support.cxf.transport.mule.org/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:pin="http://servername/pingService_v1">
<soapenv:Body>
<tns:invoke>
<pin:PingReq>
<pin:PingDB>true</pin:PingDB>
<pin:PingG>true</pin:PingG>
</pin:PingReq>
</tns:invoke>
</soapenv:Body>
</soapenv:Envelope>
A valid request would have everything except for the tns:invoke tags.
With Savon 2.x you can roll your own body by doing this:
client.call(:method, xml: "<tns:invoke><pin:PingReq> ...")
I'm currently trying to build a provisioning service of a kind for a local instance of Open-Xchange, using Rails 2.3.8, Savon and OX's SOAP API.
Via the console, I can issue the following commands that will work;
>> client = Savon::client("http://192.168.2.195/servlet/axis2/services/OXContextService?wsdl")
=> #<Savon::Client:0x2b5151bad790 #wsdl=#<Savon::Wasabi::Document:0x2b5151bad678 #document="http://192.168.2.195/servlet/axis2//services/OXContextService?wsdl", #request=#<HTTPI::Request:0x2b5151bad628>>, #http=#<HTTPI::Request:0x2b5151bad628>, #config=#<struct Savon::Config _logger=#<Savon::Logger:0x2b5151bad6c8 #device=#<IO:0x2b514a775ad0>>, pretty_print_xml=nil, raise_errors=true, soap_version=1, env_namespace=nil, soap_header=nil>>
>> client.request :list_by_database do
?> soap.body = {
?> :auth => {
?> :login => "oxadminmaster",
?> :password => "admin_master_password"
>> },
?> :db => {
?> :id => 3
>> }
>> }
>> end
HTTPI executes HTTP GET using the net_http adapter
SOAP request: https://192.168.2.195/servlet/axis2/services/OXContextService.OXContextServiceHttpsSoap11Endpoint/
SOAPAction: "urn:listByDatabase", Content-Type: text/xml;charset=UTF-8, Content-Length: 657
<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ins0="http://soap.admin.openexchange.com" xmlns:ns="http://soap.admin.openexchange.com" xmlns:ins1="http://dataobjects.soap.admin.openexchange.com/xsd" xmlns:ins2="http://dataobjects.rmi.admin.openexchange.com/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Body><ins0:listByDatabase><ins0:db><ins1:id>3</ins1:id></ins0:db><ins0:auth><ins2:login>oxadminmaster</ins2:login><ins2:password>admin_master_password</ins2:password></ins0:auth></ins0:listByDatabase></env:Body></env:Envelope>
HTTPI executes HTTP POST using the net_http adapter
warning: peer certificate won't be verified in this SSL session
SOAP response (status 200):
It works, no problems there. However, when I try the following code...
class Ox_Context_Service
extend Savon::Model
attr_accessor :data, :returnCode
document "http://192.168.2.195/servlet/axis2/services/OXContextService?wsdl"
def list_by_database oxMasterUser, oxMasterPassword
begin
response = client.request :list_by_database do
soap.body = {
:auth => {
#for the sake of testing
:login => "#{oxMasterUser}",
:password => "#{oxMasterPassword}"
},
:db => {
:id => 3
#For the sake of testing
}
}
end
if response.success?
data = response.body[:list_by_database_response][:return]
if data
#data = data
#returnCode = "#{response.http.code}"
end
end
end
rescue Savon::Error => fault
#data = {}
#returnCode = "#{fault}"
end
end
Using the following CURL...
curl localhost:4545/oxContextService/list_by_database -d 'oxUsername=oxadminmaster' -d 'oxPassword=admin_master_password'
I get...
HTTPI executes HTTP GET using the net_http adapter
SOAP request: https://192.168.2.195/servlet/axis2/services/OXContextService.OXContextServiceHttpsSoap11Endpoint/
SOAPAction: "urn:listByDatabase", Content-Type: text/xml;charset=UTF-8, Content-Length: 657
<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ins0="http://soap.admin.openexchange.com" xmlns:ns="http://soap.admin.openexchange.com" xmlns:ins1="http://dataobjects.soap.admin.openexchange.com/xsd" xmlns:ins2="http://dataobjects.rmi.admin.openexchange.com/xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Body><ins0:listByDatabase><ins0:auth><ins2:login>oxadminmaster</ins2:login><ins2:password>admin_master_password</ins2:password></ins0:auth><ins0:db><ins1:id>3</ins1:id></ins0:db></ins0:listByDatabase></env:Body></env:Envelope>
HTTPI executes HTTP POST using the net_http adapter
warning: peer certificate won't be verified in this SSL session
SOAP response (status 500):
<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><soapenv:Fault><faultcode>soapenv:Server</faultcode><faultstring>Authentication failed</faultstring><detail /></soapenv:Fault></soapenv:Body></soapenv:Envelope>
So- two identical calls, two different results. What could be going wrong here, SO?
Edit: The Rails call fails because the Auth block is sent first, instead of last. I think the new question is how to enforce XML structure order without explicitly writing out the XML.
soap.body = {
:auth => {
#for the sake of testing
:login => "#{oxMasterUser}",
:password => "#{oxMasterPassword}"
},
:db => {
:id => 3
#For the sake of testing
}
:order! => [:db, :auth]
}
The order! will force a specific order based on that array. Lesson learned: Read the manual.
I want to use a financial institution webservice to "verifyTransaction"
The method gets two strings as input and return a double as output.
double verifyTransaction (
String RefNum,
String MerchantID
)
I used Savon in rails 3.1 to call the method.
client = Savon::Client.new do |wsdl|
wsdl.document = "https://acquirer.sb24.com/ref-payment/ws/ReferencePayment?WSDL"
end
response = client.request :wsdl, "verifyTransaction" do
soap.body ={"RefNum" => "ReferenceNumber", "MerchantID" => "MymerchantId"}
end
but the I got this error:
Savon::SOAP::Fault ((env:Client) caught exception while handling request: unexpected encoding style: expected=http://schemas.xmlsoap.org/soap/encoding/, actual=)
Any thought on how to solve this?
Since I don't have valid information to actually try this, all I was able to do was get a HTTP 400 back instead of the other listed SOAP fault or a 500 response from the service.
Savon was setup with just the basics:
client = Savon::Client.new do
wsdl.document = "https://acquirer.sb24.com/ref-payment/ws/ReferencePayment?WSDL"
end
The difference I found was specifying the namespace for the specific request. Change the :wsdl out for "urn:Foo".
[26] pry(main)> client.request "urn:Foo", :verify_transaction do
[26] pry(main)* soap.body = { "RefNum" => "1", "MerchantID" => "1" }
[26] pry(main)* end
Debug output from the request:
D, [2011-10-31T09:05:17.202044 #1784] DEBUG -- : SOAP request: https://acquirer.sb24.com/ref-payment/ws/ReferencePayment
D, [2011-10-31T09:05:17.202314 #1784] DEBUG -- : SOAPAction: "verifyTransaction", Content-Type: text/xml;charset=UTF-8, Content-Length: 322
D, [2011-10-31T09:05:17.202414 #1784] DEBUG -- : <?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:urn:Foo="urn:Foo" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ins0="urn:Foo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Body><urn:Foo:verifyTransaction><MerchantID>1</MerchantID><RefNum>1</RefNum></urn:Foo:verifyTransaction></env:Body></env:Envelope>
D, [2011-10-31T09:05:17.202574 #1784] DEBUG -- : HTTPI executes HTTP POST using the httpclient adapter
D, [2011-10-31T09:05:18.780446 #1784] DEBUG -- : SOAP response (status 400):
D, [2011-10-31T09:05:18.780669 #1784] DEBUG -- :
Savon::HTTP::Error:
from /usr/local/rvm/gems/ruby-1.8.7-p334/gems/savon-0.9.7/lib/savon/soap/response.rb:100:in `raise_errors'
Longer Explanation
This is how I came up with the format above.
The namespacing can be important for some services. Looking at the wsdl carefully, this is the actual action being used since the port reference is the "PaymentIF" port:
<message name="PaymentIF_verifyTransaction">
<part name="String_1" type="xsd:string"/>
<part name="String_2" type="xsd:string"/>
</message>
Within the port definition the actual message is referenced as "tns:PaymentIF_verifyTransaction":
<portType name="PaymentIF">
...
<operation name="verifyTransaction" parameterOrder="String_1 String_2">
<input message="tns:PaymentIF_verifyTransaction"/>
<output message="tns:PaymentIF_verifyTransactionResponse"/>
</operation>
...
</portType>
So looking back at the top again, the "tns" namespace is:
xmlns:tns="urn:Foo"
I solved the problem by using SoapUI.
I've opened the WSDL in SoapUI, generate a sample requests and copy/paste it into Savon like this:
client = Savon::Client.new do |wsdl|
wsdl.document = "https://acquirer.sb24.com/ref-payment/ws/ReferencePayment?WSDL"
end
response = client.request "verifyTransaction" do
soap.xml = 'XML will be here'
end
It worked fine! :)
I'm using Savon to develop a Web Service Client. Since I'm a beginner I decided to try at first with an example WDSL, which in my case is:
http://www.webservicex.com/CurrencyConvertor.asmx?wsdl
My controller is very simple:
require 'savon'
class WebServiceController < ApplicationController
def index
puts "web_service: IN"
client = Savon::Client.new do
wsdl.document = "http://www.webservicex.com/CurrencyConvertor.asmx?wsdl"
end
response = client.request :conversion_rate do
soap.body = {
:from_currency => 'USD',
:to_currency => 'EUR'
}
end
puts response.to_hash;
render :text => response.to_hash
end
end
The XML produced by that code is:
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:wsdl="http://www.webserviceX.NET/"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<ConversionRate>
<wsdl:fromCurrency>USD</wsdl:fromCurrency>
<wsdl:toCurrency>EUR</wsdl:toCurrency>
</ConversionRate>
</env:Body>
</env:Envelope>
However, the XML should be (and I know this because I'm using soapUI):
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:web="http://www.webserviceX.NET/">
<soapenv:Header/>
<soapenv:Body>
<web:ConversionRate>
<web:FromCurrency>USD</web:FromCurrency>
<web:ToCurrency>EUR</web:ToCurrency>
</web:ConversionRate>
</soapenv:Body>
</soapenv:Envelope>
I know my XML Request isn't working because I always get '0' (zero) as response, and with the "right" XML Request generated by soapUI I get correct values (such as '0.6959' ...).
Is something missing in my code?
Thank you!!!
two things:
you need to add :wsdl to the call
you need to make sure the spelling of the tags is correct
change to
response = client.request :wsdl, :conversion_rate do
and to
"FromCurrency" => 'USD',
"ToCurrency" => 'EUR'
that should do it for you.