Попытка выяснить, как отформатировать запрос на платежи Cybersource, используя Savon и Ruby.
Я был на этом некоторое время, без удачи. Я продолжаю получать
requestMessage не поддерживается
Я предполагаю, что это связано с тем, что я передаю параметры в тело сообщения и / или заголовок не настроен правильно.
Вот ожидаемый XML для SOAP API:
<?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>
Это 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>[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>
Это ошибка, которая возвращается:
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'
Вот мой класс по рубину, который использовался, чтобы обернуть все это и сделать звонок.
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
У меня есть только пример кода 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 = "[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>
Любая помощь приветствуется.
Прежде всего позвольте мне сказать, что мои знания 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"
Я вижу, вы закомментировали параметр namespace в вашей клиентской инициализации. Это может быть способ установить пространство имен в случае, если клиент не читает его из wsdl.
Согласно этот ответ Вы можете указать различные пространства имен по мере необходимости.
Хорошо я считать Я получил это на работу. Посмотрите на этот скрипт:
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]
С этим я получаю Аутентификацию не удалось, как ожидалось.
Итак, пара вещей, которые были не правы:
Определение заголовка должно не включать Header
узел, просто начните с внутренних узлов (Security
)
run => true
в ccAuthService
является атрибутом, а не внутренним узлом.
Весь беспорядок пространства имен requestMessage
,
Других решений пока нет …