⬆️ ⬇️

Receive WebMoney without leaving the site



Hi, Habr!



I want to tell you about accepting WebMoney without going to the Webmoney merchant site (merchant.webmoney.ru). This method of accepting payments can be used, and is used in offline stores, non-browser games.



Interesting? Welcome under cat. There will be a lot of php code)



How will it look from the client:


For payment, your client will have to enter a mobile phone number \ WMID \ email. Then he will need to confirm the payment in one of 3 ways:



After payment you will need to confirm the fact of payment on your site.

')

For offline stores, the 1st and 2nd confirmation methods are most interesting.



Training:


To begin, choose a method of forming a signature, there are 3 of them:

  1. Signed with WMSigner
  2. MD5 Signature
  3. Secret Key Transfer


I will consider only the first 2 options, since when using the 3rd method, it is necessary to perform additional checks (checking the authenticity of the connection via https, in order to avoid DNS substitution):



1) WMSigner - a module that forms a signature using the WM Keeper Classic key file. When using it, it is recommended to place the config of the module and the key file above the site directory in order to avoid theft.

The archive with the module contains README.rus, in which everything is perfectly described, you should have no problems.



2) MD5

For the formation of a signature using md5, preliminary preparation is not required, in most programming languages, this hashing technology is present by default (including in php, in which we will program).



This method is preferred if you do not use other X interfaces that require only WMSigner to sign.



The signature itself will be an md5 hash from gluing

wmid + lmi_payee_purse + lmi_payment_no + lmi_clientnumber + lmi_clientnumber_type + secret_key



WMID + seller's wallet + payment_number + telephone_number \ email \ wmid_client + previous_field type + secret key



Code


As a programming language for the implementation of the interaction, I chose php. You can view and download the entire code (in one file) here .



Payment form:

<html> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> </head> <body> <form method="post" action=""> <select name="purse"> <option value="wmr">WMR</option> <option value="wmz">WMZ</option> </select><br />    WMR   :<br /> <input type="text" name="amount" VALUE="12.34"><br />   :<br /> <input type="text" name="desc" value=" "><br />  :<br /> <select name="number_type"> <option value="0"></option> <option value="1">WMID</option> <option value="2">Email</option> </select> <input type="text" name="number" value="79167777777"><br />  :<br /> <select name="confirmation_type"> <option value="1">SMS   </option> <option value="2" selected>USSD   </option> <option value="3"> /   </option> <option value="4"> </option> </select><br /> <input type="submit" value=" "> </form> </body> </html> 




For a start - 2 common functions.

 function wmsign($input) //    WMSigner { global $_SETTINGS; //    WMSigner,    . $f=proc_open("./signer/wmsigner -i ./signer/wmsigner.ini -k ./signer/".$_SETTINGS['wmid'].".kwm", array( 0=>array("pipe", "r"), 1=>array("pipe", "w"), 2=>array("file", "/tmp/error-output.txt", "a")), $pipes); if(is_resource($f)) { $output=""; //        . fwrite($pipes[0], $input); fclose($pipes[0]); //      while(!feof($pipes['1'])) { $output .= fread($pipes[1], '1024'); } //       WMSigner fclose($pipes['1']); proc_close($f); } return $output; } function post($post,$url="https://merchant.webmoney.ru/conf/xml/XMLTransRequest.asp") //  post  { $ch=curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_FAILONERROR, 1); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 3); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $post); $result=curl_exec($ch); curl_close($ch); return $result; } 




We check the input parameters, compose an XML request to WebMoney, sign it and send it. Next, check the status of the request, if everything is without errors - we issue a form to confirm the payment. If SMS - then we ask also the code received by SMS.



 $_SETTINGS=array( 'wmid'=>'123456789012', // wmid 'wmr'=>'R123456789012', // R  'wmz'=>'R123456789012', // Z  'amount'=>'123.45', //  'desc'=>' X20', //  'sing_method'=>'sing', //  , md5\sing (by default)\secret_key 'secret_key'=>'t4er43d#4' //  ,     . ); if($_SERVER['REQUEST_METHOD']=="POST") { //     .    -   $error = True; switch($_REQUEST['purse']) //   ,    ,     .   - WMZ { case "wmr": $purse=$_SETTINGS['wmr']; break; default: $purse=$_SETTINGS['wmz']; } if(!(is_numeric($_REQUEST['number_type'])&&$_REQUEST['number_type']<3&&$_REQUEST['number_type']>=0)) //   s { $error=True; $errors['number_type']=" "; } if(!(is_numeric($_REQUEST['confirmation_type'])&&$_REQUEST['confirmation_type']<=4&&$_REQUEST['confirmation_type']>0)) //    { $error=True; $errors['confirmation_type']=" "; } $amount=(is_numeric($_POST['amount']))?$_POST['amount']:$_SETTINGS['amount']; if(!$error) { $pay_no=time(); //   $xml=new DomDocument('1.0', 'utf-8'); $xml->formatOutput=true; //   $merchant=$xml->appendChild($xml->createElement('merchant.request')); $merchant->appendChild($xml->createElement('wmid'))->appendChild($xml->createTextNode($_SETTINGS['wmid'])); // WMID $merchant->appendChild($xml->createElement('lmi_payee_purse'))->appendChild($xml->createTextNode($purse)); //   $merchant->appendChild($xml->createElement('lmi_payment_no'))->appendChild($xml->createTextNode($pay_no)); //       $merchant->appendChild($xml->createElement('lmi_payment_amount'))->appendChild($xml->createTextNode($amount)); //       ,  - . $merchant->appendChild($xml->createElement('lmi_payment_desc'))->appendChild($xml->createTextNode($_SETTINGS['desc'])); //  , 255 . $merchant->appendChild($xml->createElement('lmi_clientnumber'))->appendChild($xml->createTextNode($_REQUEST['number'])); //   (  79167777777,380527777777), WMID, email. $merchant->appendChild($xml->createElement('lmi_clientnumber_type'))->appendChild($xml->createTextNode($_REQUEST['number_type'])); //  ,   lmi_clientnumber (0-, 1-WMID, 2-email) $merchant->appendChild($xml->createElement('lmi_sms_type'))->appendChild($xml->createTextNode($_REQUEST['confirmation_type'])); //    (1-, 2-USSD, 3- ,           ..,   - , 4-  WM ) $merchant->appendChild($xml->createElement('secret_key'))->appendChild($xml->createTextNode('')); //    (1-, 2-USSD, 3- ,           ..,   - , 4-  WM ) //         switch($_SETTINGS['sign_method']) { case "md5": // md5,   ,        md5     $sign = md5($_SETTINGS['wmid'].$purse.$pay_no.$_REQUEST['number'].$_REQUEST['number_type'].$_SETTINGS['secret_key']); $merchant->appendChild($xml->createElement('md5'))->appendChild($xml->createTextNode($sign)); break; case "sign": //    WMSigner //     ,      . $sign = wmsign($_SETTINGS['wmid'].$purse.$pay_no.$_REQUEST['number'].$_REQUEST['number_type']); $merchant->appendChild($xml->createElement('sign'))->appendChild($xml->createTextNode($sign)); break; } //     WebMoney,  POST $result=post($xml->saveXML(),"https://merchant.webmoney.ru/conf/xml/XMLTransRequest.asp"); //   dom, ..    XML  $dom=new domDocument; $dom->loadXML($result); if(!$dom) die("  XML"); $data=simplexml_import_dom($dom); //    (retval -  , 0    ),      . if($data->retval!=0) { echo $data->retval." Error: ".$data->userdesc."\n\n"; } else { ?> <html> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> </head> <body> <form method="post" action=""> <input type="hidden" name="confirmation" value="1"> <?= ($data->realsmstype==1)?' ,   <br />\n':'' ?><input type="<?= ($data->realsmstype==1)?'text':'hidden' ?>" name="code" value="<?= ($data->realsmstype==1)?'':0 ?>"> <input type="hidden" name="account" value="<?=$data->operation['wminvoiceid']?>"> <input type="hidden" name="purse" value="<?= $_REQUEST['purse'] ?>"> <input type="submit" value=" ."> </form> </body> </html> <? } } 


The client confirms the payment (if SMS - enters the code that came to him, with the remaining options - just press the button), and we have to notify WebMoney about the payment. Otherwise, we can not see the money.

 //  ,       . if(!$error) { $xml=new DomDocument('1.0','utf-8'); $xml->formatOutput = true; //   $merchant = $xml->appendChild($xml->createElement('merchant.request')); $merchant->appendChild($xml->createElement('wmid'))->appendChild($xml->createTextNode($_SETTINGS['wmid'])); // WMID $merchant->appendChild($xml->createElement('lmi_payee_purse'))->appendChild($xml->createTextNode($purse)); //   $merchant->appendChild($xml->createElement('lmi_clientnumber_code'))->appendChild($xml->createTextNode($_REQUEST['code'])); //  ,    (WM \USSD = 0, -1  ) $merchant->appendChild($xml->createElement('lmi_wminvoiceid'))->appendChild($xml->createTextNode($_REQUEST['account'])); //  ,     switch($_SETTINGS['sign_method']) { case "md5": // md5,   ,        md5     $sign=md5($_SETTINGS['wmid'].$purse.$_REQUEST['account'].$_REQUEST['code'].$_SETTINGS['secret_key']); $merchant->appendChild($xml->createElement('md5'))->appendChild($xml->createTextNode($sign)); break; case "sign": //    WMSigner //     ,      . $sign=wmsign($_SETTINGS['wmid'].$purse.$_REQUEST['account'].$_REQUEST['code']); $merchant->appendChild($xml->createElement('sign'))->appendChild($xml->createTextNode($sign)); break; } //     WebMoney,  POST $result=post($xml->saveXML(),"https://merchant.webmoney.ru/conf/xml/XMLTransConfirm.asp"); //   dom, ..    XML  $dom=new domDocument; $dom->loadXML($result); if(!$dom) die("  XML"); $data=simplexml_import_dom($dom); //    (retval -  , 0    ),      . if($data->retval!=0) { echo "Error№".$data->retval.": ".$data->userdesc."\n\n"; ?> <html> <head> <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> </head> <body> <form method="post" action=""> <input type="hidden" name="confirmation" value="1"> <?=(!$_POST['code'])?' ,   <br />':''?> <input type="<?=(!$_POST['code'])?'text':'hidden'?>" name="code" value="<?=$_POST['code'];?>"> <input type="hidden" name="account" value="<?=$_POST['account']?>"> <input type="hidden" name="purse" value="<?= $_REQUEST['purse'] ?>"> <input type="submit" value=" ."> </form> </body> </html> <? } else { //     . echo "  "; } } 




Test results


Actually, I could not receive a payment using the X20 interface. Constantly crashed errors. Or

The VM-identifier you specified was found, but there is not enough money on its wallet


or

Message from WebMoney Transfer: at the moment your seller has stopped accepting payments, please try again later.




Although there is money in the account, payment acceptance is not suspended. Now I am waiting for a comment from WM about these errors, and about the commission when using X20.



Problems solved. The reason is in the fixed commission, inability to pay from the wallets registered in the merchant and not quite true logic. If you choose a payment method - a WM-account, then the payment amount on the wallet + 0.8% commission + fixed commission will be checked. Although a fixed commission on a WM-account does not apply.



Related Links


X20 Interface Description

Wmsigner

Video showing payment via X20

Video showing payment via X20 in offline stores

An example of implementation on the merchant's website



UPD When confirming payment via USSD \ SMS an additional fixed fee is charged - 0.9 WMR, 0.04 WMZ, 0.03 WME, 0.25 WMU.

Source: https://habr.com/ru/post/121182/



All Articles