Why did I need it?
The project strongly demanded the connection of convenient payment systems. Yes, there is webmoney, but not at all. Yes, there are moneybookers for cards, but the bureaucratic process is too long.
It was decided to accept payments through QIWI, firstly because their machines are almost everywhere, and secondly (shhh, great secret!) They are preparing to launch a direct payment system from the account of the cellular operator, without any stupid SMS and ninety percent commissions.
Well, since manually requesting registries from the payment system and entering data into the accounting department and the website yesterday, a fully automatic connection option using the SOAP protocol was chosen.
')
No sooner said than done!
For the site was taken quite ordinary VDS, which is assembled as an ordinary server set - nginx in front, Apache behind.
I already had a basis in the form of some kind of CMS, including in it the module of users' personal accounts with plug-ins of payment systems was implemented.
The easiest way to work with SOAP is to take the nuSOAP class from google code (
http://code.google.com/p/nusoap-for-php5/ ).
Then we need to do two things:
- Sending payment attempt information to QIWI server
- Reception from QIWI to our server package with information about changing the status of the payment.
Documentation, it’s a pity that it’s not very detailed, is here:
https://ishop.qiwi.ru/docs/OnlineStores_Protocols_SOAP.pdfI give my code, which everyone can take as a basis
$client = new nusoap_client("https://mobw.ru/services/ishop", false); // QIWI
$error = $client->getError();
if ( !empty($error) ) {
TPAccount::cancelTransaction($transaction); //
echo -1;
exit();
}
$client->useHTTPPersistentConnection();
// :
// ID QIWI
//
//
//
// ,
//
//
//
// 0 – QIWI, 1 –
$params = array(
'login' => $payment->getVar('id'),
'password' => $payment->getVar('password'),
'user' => $phone,
'amount' => $transaction['amount'],
'comment' => ' ',
'txn' => $transaction['id'],
'lifetime' => date("dmY H:i:s", strtotime("+2 weeks")),
'alarm' => 1,
'create' => 1
);
// :
$result = $client->call('createBill', $params, "http://server.ishop.mw.ru/");
if ($client->fault) {
TPAccount::cancelTransaction($transaction);
echo -1;
exit();
} else {
$err = $client->getError();
if ($err) {
TPAccount::cancelTransaction($transaction);
echo -1;
exit();
} else {
echo $result;
exit();
}
}
My script is called via AJAX, and, as can be seen from the code, it returns the exit code: -1 is an error, any other value is the response of the QIWI server.
The code for receiving information on status changes from the payment system is a little more complicated (I quote immediately in final form):
$server = new nusoap_server;
$server->register('updateBill');
$server->service($HTTP_RAW_POST_DATA);
function updateBill($login, $password, $txn, $status) {
// qiwiPayment
$paymentclass = "qiwi" . "Payment";
$payment = new $paymentclass;
//
if ( $login != $payment->getVar('id') )
return 150;
if ( !empty($password) && $password != md5($payment->getVar('password')) )
return 150;
//
$transaction = TPTransactions::getTransactionByID(intval($txn));
if ( empty($transaction) )
return 300;
switch ( intval($status) ) {
case 50:
// Bill created
return 0;
break;
case 60:
// –
TPAccount::proceedTransaction($transaction);
return 0;
break;
default:
// - , ,
TPAccount::cancelTransaction($transaction);
return 0;
break;
}
return 300;
}
Surprise first. nuSOAP.
It seems that the code was written, verified on a local server, it's time to introduce my PHP to noble JAVA from the QIWI side.
The first surprise occurs instantly - at the first attempt to create a test payment, nuSOAP puzzles with the message “Response not of type text / xml”. Yes, the creators of nuSOAP did not expect that one Russian payment system would decide that SOAP is “application / soap + xml”. We will correct them - it is rather simple to find and comment out the corresponding checks inside the nuSOAP code.
And here is a triumph - a test payment was created, it can be seen in QIWI, it is time to accept the answer about successful payment from it.
I go to the machine ...
Nginx configuration Error 411.
However, I am in for an unpleasant surprise. When I try to check the web service of the store, my server unexpectedly responds with error 411 - Length required.
The QIWI support response clarified everything:
"Hello. Our service sends chunked requests without specifying content-length. I think your web server should have settings to handle such requests. ”For me, it remained forever a mystery that prevents QIWI from specifying Content-length, but now much has become understood. nginx does not accept chuncked requests, immediately responding to them with an error ...
Go to the server and work!
Download the module NginxHttpChunkinModule, put it, unpacking, for example in / root / chunkin. You can get it here:
http://wiki.nginx.org/NginxHttpChunkinModule , there are also installation instructions
There is nothing difficult in installation, I will show on the example of FreeBSD and nginx 0.7.64
#cd /usr/ports/www/nginx
# cd ./work/nginx-0.7.64
# ./configure --add-module=/root/chunkin
# cd ../../
#make rmconfig && make config && make deinstall && make install
Then we put in the nginx config (usually this /usr/local/etc/nginx/nginx.conf) in the http parameter section
chunkin on;
and restart nginx
Another surprise - Error 501
And what do you think? Did it work? Of course not.
The next scourge was the error 501 Method Not Implemented. Its cause was managed to be localized fairly quickly by looking at the server logs. Despite the fact that I registered the address of my web service /account/result/qiwi.html in the QIWI interface, the QIWI client stubbornly fought through a closed door - it tried to make a request for the nonexistent address / account / result / qiwi /
What caused this behavior - it was not possible to find out, but one rule of mod_rewrite saved the situation. The request began to come to the address I needed. And then QIWI support quickly solved this problem on its side.
The last and most terrible surprise
Well, now that everything seems to be ready, QIWI has prepared me another surprise. When I tried to test the web service of the store from the QIWI interface, I invariably received the message “An error occurred during the request: error in msg parsing: xml was empty, did not parse!”
In fact, this is a good sign - it means that nuSOAP works, and even gives the correct SOI response to a request from the QIWI point of view. But he doesn’t see any data from QIWI ...
Debugging showed that the $ _POST array is empty, no data comes from QIWI.
Here I fell into a complete stupor. There is no obvious reason for this behavior, but it still exists!
Building nginx with the --with-debug parameter, a two-day analysis of the logs, consulting nginx experts (many thanks to comrade MiksIr) showed a rather unattractive picture. Nginx with the installed chunkin module really honestly collected chuncked data into a single unit and prepared for further transfer to Apache a request with the correctly specified Content-length header. However, instead of completely removing the Transfer-encoding header from the original request: it chuncked (the chunkin module) just cut off its value, leaving “Transfer-encoding:” like this. Apache at such a disgrace quite logical cursing.
To say that the problem was quickly - I can not. However, she did decide. So:
Go to the sources of the module NginxHttpChunkinModule, file ngx_http_chunckin_filter_module.c and comment on the following lines:
r->headers_in.transfer_encoding->value.len = 0;
r->headers_in.transfer_encoding->value.data = (u_char*) "";
ngx_http_chunkin_clear_transfer_encoding(r);
There was no time to understand the code deeply, so it can be considered a rude but effective hack.
Rebuild nginx again, and - voila!
The surprise is quite the last
I create a transaction, I see its number. I’m going to QIWI’s personal account and trying to manually set the status to “Paid”. The QIWI test script faithfully shows me 0 (zero), but nothing happens ...
I will not bore readers with the description of the process of finding this glitch. Let me just say that the test script always returns zero, regardless of the response from your server, this is “a
feature of the system (c) QIWI support”, but to take into account the fact that the client part of a payment system written in Java is considered to be a duty to transfer Hash your password in uppercase - must be.
Total.
Everything works, the site has been launched, an invaluable experience has been gained, which, in turn, I am ready to share with pleasure with habrauser (by the
way, meet — this is my first post on Habré! ) And habrachchiteli.
Thanks in advance, I hope that my article will help someone save at least a few minutes of precious time!
PS The link to the project could give - yes I am afraid of habraeffekt. Maybe later, in the appropriate blog, when I am sure that the server will withstand ...
UPD. Transferred to the blog "Payment Systems"