Что нового

Инструкция или концепция продажи своего ПО

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
"Лучшие идеи обычно рождаются из потребностей одного человека" ©

  • Данная статья не затрагивает вопрос о защите вашего ПО (имеется в виду де-компиляция и т.п.).
  • В статье предполагается, что пользователь имеет базовые знания в php, xml, и веб-кодинге в целом, про AutoIt молчу :smile:.
  • Методы реализации скриптов и обмена данными следует воспринимать как пример, их желательно изменить для использования на деле (проявите изобретательность и творчество). Потому как здесь они уже представлены на публичное обозрение, и это сильно ставит под вопрос надёжность, т.к зная алгоритм будет не сложно взломать, но это уже отдельная тема.
  • Ссылки по материалам для данной статьи найдёте в конце.



С недавнего времени я всерьёз задумался над тем, что раньше для меня казалось немыслимо - продавать свои программы.
Но как оказалось, это довольно непростое и кропотливое занятие.

В конце этой статьи есть относительно простой вариант реализации данной задумки, но вариант представленный ниже, мне кажется более надёжным и интересным.

Что для этого нужно?
[list type=decimal]
[*]Свой сервер (хранение базы данных, исполнение php скриптов)
[*]Свой сайт (для размещения своих продуктов на продажу)
[*]Регистрация систем электронных денег (WebMoney, PayPal, Yandex.Money, и т.п.)
[*]Регистрация в платёжной системе (в данной статье рассматривается http://digiseller.ru - для упрощения процедуры продажи продукта)
[*]Хорошая программа с уникальным функционалом
[*]Энтузиазм и маркетинговые способности
[*]Свободное время (много)
[/list]

Теперь собственно разберём по частям, что требуется сделать:

- Сервер -
[list type=upper-roman]
[*]На сервере создаём базу данных, допустим назовём её MySoft.
[*]Далее на сервер нужно загрузить два php скрипта:
product_register.php - Отправка ответного xml запроса сервису
PHP:
<?php

$oXML = new SimpleXMLElement($GLOBALS['HTTP_RAW_POST_DATA']);

$sID = $oXML->id;
$sINV = $oXML->inv;
$sSIGN = $oXML->sign;
$sEMAIL = $oXML->email;
$sDATA = $oXML->data;

$xml = '<?xml version="1.0" encoding="windows-1251"?>
 <response>
 <id>' . $sID . '</id>
 <inv>' . $sINV . '</inv>
 ';

$result = False;

//Check the signature (to recognize DigiSeller service)
if (md5($sID . ':' . $sINV . ':digiseller_pass') == $sSIGN) {
  $sKey = GenerateProductKey($sDATA); //Generate key base on $sDATA (it's client hardware id passed by the DigiSeller system - entered by the client)
  
  $xml .= ' <goods>
      <![CDATA[
      Your Product Key: <b>' . $sKey . '</b>

      This key is unique for you only (hardware attached),

      please keep the payment receipt (' . $sINV . ') to restore your key on other machine.
      ]]>
   </goods>
   ';
  
   //Connect to database (named as product id) and add row with registration data
  $db_hostname = "myserver.ru"; 
  $db_username = "username";
  $db_password = "password";
  $db_name = "db_name";
  $db_table = $sID;
  
  $hDB = mysql_connect($db_hostname, $db_username, $db_password); // or die("Unable to connect to MySQL");
  $hSel = mysql_select_db($db_name, $hDB); // or die("Could not select examples");
  
  $query = "CREATE TABLE IF NOT EXISTS `" . $db_table . "` (
    `email` VARCHAR(100) NOT NULL,
    `product_inv` INT(50) NOT NULL,
    `hwid` VARCHAR(50) NOT NULL,
    `activation_time` VARCHAR(500) NOT NULL,
    `active` INT(1) NOT NULL DEFAULT '1')";
  
  mysql_query($query);
  
  $query = "INSERT INTO `" . $db_table . "` (
    `email`,
    `product_inv`,
    `hwid`,
    `activation_time`,
    `active`) VALUES ('" . $sEMAIL . "', " . $sINV . ", '" . $sKey . "', '" . date("Y-m-d H:i:s") . "', 1);";
   
  $result = mysql_query($query);
  mysql_close($hDB);
}

if(!$result){
  $xml .= '<error>
    <![CDATA[
    <b>Wrong Product ID or xml request!</b>
    ]]>
 </error>';
}

$xml .= '</response>';

echo $xml;

function GenerateProductKey($sData)
{
  $sRet_PK = '';
  $aSplit_PK = str_split(preg_replace('~[^A-Z0-9]+~', '', strtoupper($sData)));
	
	$iStep = 1;
	$i = count($aSplit_PK);
	
	foreach (array_reverse($aSplit_PK) as $sPart) {
    if($iStep == 1)
    {
      $iStep = 0;
      
      if(($i % 5) == 0)
      {
        $sRet_PK .= $sPart . '-';
      } else {
        $sRet_PK .= $sPart;
      }
    } else {
      $iStep = 1;
    }
    
    $i -= 1;
  }
	
	return preg_replace('~\A-+|-+$|\A.{1,4}-|-.{1,4}$~', '', $sRet_PK);
}

?>
- Здесь меняем "digiseller_pass" на ваш пароль от системы digiseller, это нужно для проверки достоверности с запросом от системы (чтобы предотвратить ложный вызов скрипта).
- На этот скрипт будет отправляться запрос xml с сервиса digiseller, со всеми указанными параметрами (после оплаты продукта).
- Скрипт сам создаст таблицу (имя таблицы это id продукта в системе) в указанной базе со следующими полями:
emailproduct_invhwidactivation_timeactive
email покупателяномер оплаченного счёта в системе учёта DIGISELLERключ продукта (привязка ПО к железу)Дата активацииСостояние ключа продукта (активный или нет)

db_cmd.php - Интеракция с базой данных (не рекомендуется делать это из AutoIt-скрипта, т.к в коде будут явно указаны данные доступа к базе данных
PHP:
<?php

$sFNC = $_GET['fnc'];
$sID = $_GET['id'];
$sINV = $_GET['inv'];
$sPK = strrev($_GET['pk']);
$sPK = substr($sPK, 4) . substr($sPK, 0, 4); //Assembl to real Product Key string

//Connect to database (named as product id) and add row with registration data
$db_hostname = "myserver.ru"; 
$db_username = "username";
$db_password = "password";
$db_name = "db_name";
$db_table = $sID;

$hDB = mysql_connect($db_hostname, $db_username, $db_password) or die("ERROR: Unable to connect to MySQL");
mysql_select_db($db_name, $hDB) or die("ERROR: Could not select examples");

$sRet = $sFNC($db_table, $sINV, $sPK);
echo $sRet;

mysql_close($hDB);

function get_inv($db_table, $sINV, $sPK)
{
  $query = "SELECT product_inv FROM `" . $db_table . "` WHERE `hwid`='" . $sPK . "' AND `active`=1";
  $result = mysql_query($query);
  
  if (!$result) return "ERROR: " . mysql_error();
  if (mysql_num_rows($result) == 0) return "ERROR: hwid not found or product_inv not active";
  
  while ($row = mysql_fetch_assoc($result)) {
    return $row['product_inv'];
  }
}

function validate_inv($db_table, $sINV, $sPK)
{
  $sTimes = '';
  
  $query = 'SELECT * FROM `' . $db_table . '` WHERE `product_inv`=' . $sINV . ' AND `active`=1 LIMIT 1';
  $result = mysql_query($query);
  
  if (!$result) return "ERROR: " . mysql_error();
  if (mysql_num_rows($result) == 0) return "ERROR: Invalid or inactive product_inv";
  
  while ($row = mysql_fetch_assoc($result)) {
     $sTimes = $row['activation_time'];
  }
  
  //Check if this product was licensed before
	$aSplit_Times = explode('|', $sTimes);
	
	//More than 10 times this soft was licensed
	if (count($aSplit_Times) > 10) {
		//If from first date to the last date passed less than 10 days, we deactivate this license (it's suspicious behaviour)
		
		$datetime1 = new DateTime($aSplit_Times[1]);
		$datetime2 = new DateTime($aSplit_Times[count($aSplit_Times)-1]);
		
		$iDiff = $datetime1->diff($datetime2);
		
		if ($iDiff->format('%a') < 10) {
			deactivate_inv($db_table, $sINV, $sPK);
			return "NO";
		}
	}
	
	//We activate the inv - set new hwid, and add datetime to the activation_time column (to check later for hacking/multiple usage of product_inv)
	return activate_inv($db_table, $sINV, $sPK);
}

function activate_inv($db_table, $sINV, $sPK)
{
  $sTimes = '';
  
  $query = 'SELECT * FROM `' . $db_table . '` WHERE `product_inv`=' . $sINV . ' AND `active`=1 LIMIT 1';
  $result = mysql_query($query);
  
  if (!$result) return "ERROR: " . mysql_error();
  if (mysql_num_rows($result) == 0) return "ERROR: Invalid or inactive product_inv";
  
  while ($row = mysql_fetch_assoc($result)) {
    $sTimes = $row['activation_time'];
  }
  
  $sTimes .= ($sTimes !== '' ? '|' : '') . date("Y-m-d H:i:s");
  
  $query = "UPDATE `" . $db_table . "` SET `hwid`='" . $sPK . "' WHERE `product_inv`=" . $sINV . " AND `active`=1";
  $result = mysql_query($query);
  
  if (!$result) return "ERROR: " . mysql_error();
  //if (mysql_affected_rows() == 0) return "ERROR: Invalid or inactive product_inv";
  
  $query = "UPDATE `" . $db_table . "` SET `activation_time`='" . $sTimes . "' WHERE `product_inv`=" . $sINV . " AND `active`=1";
  $result = mysql_query($query);
  
  if (!$result) return "ERROR: " . mysql_error();
  if (mysql_affected_rows() == 0) return "ERROR: Invalid or inactive product_inv";
  
  return "OK";
}

function deactivate_inv($db_table, $sINV, $sPK)
{
  $query = "UPDATE `" . $db_table . "` SET `active`=0 WHERE `product_inv`=" . $sINV . " AND `active`=1";
  $result = mysql_query($query);
  
  if (!$result) return "ERROR: " . mysql_error();
  if (mysql_affected_rows() == 0) return "ERROR: Invalid or inactive product_inv";
  
  return "OK";
}

?>
- В этот скрипт будут посылаться запросы с AutoIt-скрипта в виде команд, в соответствий с командой будут возвращаться данные или выполняться действия с базой данных.
- См. AutoIt-скрипт далее чтобы понять суть скрипта.
[/list]

- Сайт -
[list type=upper-roman]
[*]Тут ничего сложного, создаёте свой сайт, где будет размещаться интернет магазин с вашими продуктами (такую возможность предоставляет сервис digiseller).
[/list]

- Системы электронных денег -
[list type=upper-roman]
[*]Регистрируете электронные деньги, такие как WebMoney, PayPal, Yandex.Money и другие. Стоит отметить, что при регистрации лучше сразу заполнять все данные и получать нужные сертификаты (см. справку для каждой платёжной системы отдельно), позже это упростит всю процедуру получения средств.
[/list]

- Платёжная система (digiseller) -
[list type=upper-roman]
[*]Регистрируетесь, создаёте свой магазин, получаете код для вставки на своём сайте (можно вставить на любой странице - это и будет страница вашего интернет магазина).
[*]Далее создаёте категорию своего ПО, и добавляете товар, где...
[list type=lower-alpha]
[*]Тип товара: ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ (ПО)
[*]После оплаты покупатель получит: форму для заполнения регистрационных данных
[*]Торговая площадка: только в собственном магазине
[*]Мой магазин: указываете магазин из списка
[*]Теперь заполняете поля...
[list type=square]
[*]название программы
[*]trial version (free) - тут указываете ссылку на демо версию, где полный функционал активируется только после приобретения ключа
[*]цена
[*]описание программы
[/list]

[/list]


[*]Далее (в форме) указываете:
[box]
заполняемые поля:
[box]
комментарий к полю:имя поля:заполнять:
Data from your product (from demo version program)dataОбязательно
Email addressemailОбязательно
[/box]

метод отправки: XML-запрос
URL для передачи XML-запроса: http://myserver.ru/product_register.php (это ваш php скрипт выше, который будет принимать xml запрос)


[/box]

[*]Указываете платёжные системы (те что регистрировали ранее).
[/list]

- Ваша программа -
[list type=upper-roman]
[*]Очень важно чтобы программа была востребована, но об этом читайте в статьях ниже.
[*]Чуть ниже описание и пример реализации вашей программы (что касается автоматизации регистрации).
[/list]

- Маркетинг -
[list type=upper-roman]
[*]Приложите усилия чтобы распространить ваш продукт и продвинуть его на разных площадках и программных платформах.
[/list]

- Время -
[list type=upper-roman]
[*]Ну, тут в принципе нечего сказать, постарайтесь найти его, причём в больших количествах!
[/list]

___________________________________
Программа на AutoIt
Реализация не сложная, ниже приведён пример программы (скрипт), который при запуске проверяет регистрационные данные.
[list type=upper-roman]
[*]При первом запуске (демо версия), скрипт предлагает зарегистрировать продукт, после удачной покупки покупатель получает ключ продукта который указывает в поле Product Key.
[*]Если покупатель со временем решит перенести свой продукт на другой компьютер, то в программе предусмотрено восстановление ключа продукта (если покупатель потерял свой файл ключей, то он может указать номер квитанции полученный при покупке в системе digiseller и получить новый ключ продукта - привязанный к новому компьютеру).
[*]Ключ продукта деактивируется в случае частых запросов восстановления, это может предотвратить совместное использование одной купленной лицензии.
[/list]

Код:
#include <WinAPIDiag.au3>
#include <APIDiagConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <InetConstants.au3>

Global $sPRODUCT_NAME		= 'My Program'
Global $sPRODUCT_ID			= '12345' ;This is your product id from DigiSeller service
Global $sPRODUCT_URL		= 'https://www.oplata.info/asp2/pay_pp.asp?id_d=' & $sPRODUCT_ID
Global $sDBCMD_URL			= 'http://myserver.ru/db_cmd.php'
Global $sPRODUCT_INV		= _ProductKey_Read('inv')
Global $sPRODUCT_HWID		= _ProductKey_Read('hwid')
Global $fAPP_LICENSED 		= _ProductKey_Validate()

If $fAPP_LICENSED Then
	MsgBox(64, 'Title', 'Full version code')
	Exit
Else
	_RegisterGUI()
EndIf

;.....

If $fAPP_LICENSED Then
	MsgBox(64, 'Title', 'Full version code (after registration)')
EndIf

Func _RegisterGUI()
	Local $hGUI, $iRegister_Bttn, $iData_Input, $iCopy_Bttn, $iPK_Input, $iRestorePK_Bttn, $iOk_Bttn, $iCancel_Bttn
	Local $fHWID_Restored = False
	
	$hGUI = GUICreate('Registration...', 500, 300)
	
	GUICtrlCreateLabel('To use full version of this program, you must register', 20, 20)
	GUICtrlCreateLabel('Copy DATA bellow and press "Register..." button', 20, 40)
	$iRegister_Bttn = GUICtrlCreateButton('Register...', 20, 70, 80, 20)
	GUICtrlCreateLabel('After successful payment you will be asked to set DATA field', 120, 72)
	GUICtrlCreateLabel('DATA:', 20, 113)
	$iData_Input = GUICtrlCreateInput(_ProductKey_GetHardwareData(), 60, 110, 340, 20, BitOR($GUI_SS_DEFAULT_INPUT, $ES_READONLY))
	GUICtrlSetState(-1, $GUI_FOCUS)
	$iCopy_Bttn = GUICtrlCreateButton('Copy', 420, 110, 60, 20)
	
	GUICtrlCreateLabel('Product Key:', 20, 203)
	$iPK_Input = GUICtrlCreateInput('', 100, 200, 380, 20)
	$iRestorePK_Bttn = GUICtrlCreateButton('Restore product key...', 100, 220, 120, 20)
	
	$iOk_Bttn = GUICtrlCreateButton('OK', 20, 270, 60, 20)
	$iCancel_Bttn = GUICtrlCreateButton('Cancel', 100, 270, 60, 20)
	
	GUISetState(@SW_SHOW, $hGUI)
	
	While 1
		$nMsg = GUIGetMsg()
	
		Switch $nMsg
			Case $GUI_EVENT_CLOSE, $iCancel_Bttn
				If $fHWID_Restored Then
					Local $iAsk = MsgBox(52, 'Attention', _
						'Are you sure? you already have restored the product key...' & @CRLF & @CRLF & _
						'* If you cancel now, the restore point will be registered, many restore points in short time can cause license to be deactivated *', _
						0, $hGUI)
					
					If $iAsk <> 6 Then
						ContinueLoop
					EndIf
				EndIf
				
				$fAPP_LICENSED = False
				
				_ProductKey_Delete('hwid')
				_ProductKey_Delete('inv')
				
				ExitLoop
			Case $iRegister_Bttn
				ShellExecute($sPRODUCT_URL)
			Case $iCopy_Bttn
				ClipPut(GUICtrlRead($iData_Input))
			Case $iRestorePK_Bttn
				If $fHWID_Restored Then
					Local $iAsk = MsgBox(52, 'Attention', _
						'Are you sure? you already have restored the product key...' & @CRLF & @CRLF & _
						'* If you restore again, the restore point will be registered, many restore points in short time can cause license to be deactivated *', _
						0, $hGUI)
					
					If $iAsk <> 6 Then
						ContinueLoop
					EndIf
				EndIf
				
				$sInv = InputBox('Restore...', 'Please enter your payment receipt number (product_inv):', '', '', -1, -1, Default, Default, 0, $hGUI)
				
				If $sInv <> '' Then
					$sPRODUCT_INV = $sInv
					$sPRODUCT_HWID = 'OLD_HWID'
					
					$fAPP_LICENSED = _ProductKey_Validate()
					
					If $fAPP_LICENSED Then
						GUICtrlSetData($iPK_Input, $sPRODUCT_HWID)
						$fHWID_Restored = True
					Else
						MsgBox(48, 'Error', 'Sorry, unable to restore your Product key, wrong payment receipt number (product_inv), or it''s blocked.', 0, $hGUI)
						$fHWID_Restored = False
					EndIf
				EndIf
			Case $iOk_Bttn
				$sPRODUCT_HWID = GUICtrlRead($iPK_Input)
				$fAPP_LICENSED = _ProductKey_Validate()
				
				If $fAPP_LICENSED Then
					_ProductKey_Write('hwid', $sPRODUCT_HWID)
					_ProductKey_Write('inv', $sPRODUCT_INV)
					
					MsgBox(64, 'Successful!', 'Now your product is licensed!', 0, $hGUI)
					
					ExitLoop
				EndIf
				
				MsgBox(48, 'Error', 'Sorry, wrong Product Key', 0, $hGUI)
		EndSwitch
	WEnd
	
	GUIDelete($hGUI)
EndFunc

Func _ProductKey_Validate()
	Local $sPK = _ProductKey_GenerateKey()
	
	;If hwid from registry match the current hardware id, then this soft is licensed
	If $sPRODUCT_HWID == $sPK Then
		;Set once the product_inv data
		If $sPRODUCT_INV = '' Then
			;Get product_inv data from our db_cmd.php wrapper
			Local $sInv = _ProductKey_DBRequest('get_inv', $sPRODUCT_ID, $sPRODUCT_INV, $sPK)
			
			;product_inv may be blocked
			If @error Then
				Return False
			EndIf
			
			_ProductKey_Write('inv', $sInv)
		EndIf
		
		Return True
	EndIf
	
	;If product_inv or product_hwid not found, then this soft is not licensed
	If $sPRODUCT_INV = '' Or $sPRODUCT_HWID = '' Then
		Return False
	EndIf
	
	Local $sValidate = _ProductKey_DBRequest('validate_inv', $sPRODUCT_ID, $sPRODUCT_INV, $sPK)
	
	;@error will indicate that there was an error to connect to the database (you can use it to inform the user to contact the author)
	If @error Then
		Return SetError(1, 0, False)
	EndIf
	
	If $sValidate = 'OK' Then
		$sPRODUCT_HWID = $sPK
		_ProductKey_Write('hwid', $sPK)
		Return True
	Else
		_ProductKey_Delete('hwid')
		Return False
	EndIf
EndFunc

Func _ProductKey_DBRequest($sFunc, $sID, $sINV, $sPK = '')
	If $sPK <> '' Then
		;Some sort of crypting method (decrypted later by db_cmd.php script)
		$sPK = _ProductKey_Crypt($sPK)
	EndIf
	
	Local $sRet = InetRead($sDBCMD_URL & '?fnc=' & $sFunc & '&id=' & $sID & '&inv=' & $sINV & '&pk=' & $sPK, $INET_FORCERELOAD)
	Local $iError = @error
	
	$sRet = BinaryToString($sRet)
	
	If $iError Or StringInStr($sRet, 'ERROR: ', 2) Then
		Return SetError(1, 0, $sRet)
	EndIf
	
	Return $sRet
EndFunc

Func _ProductKey_GenerateKey($sHWID = '')
	Local $sPK_Val = $sHWID
	
	If $sHWID = '' Then
		$sPK_Val = _ProductKey_GetHardwareData()
	EndIf
	
	Local $sRet_PK, $aSplit_PK = StringSplit(StringRegExpReplace(StringUpper($sPK_Val), "[^A-Z0-9]+", ""), "")
	
	For $i = $aSplit_PK[0] To 1 Step -2
		If Mod($i, 5) = 0 Then
			$sRet_PK &= $aSplit_PK[$i] & "-"
		Else
			$sRet_PK &= $aSplit_PK[$i]
		EndIf
	Next
	
	Return StringRegExpReplace($sRet_PK, "\A-+|-+$|\A.{1,4}-|-.{1,4}$", "")
EndFunc

Func _ProductKey_GetHardwareData()
	Local $sUniqueHwID = _WinAPI_UniqueHardwareID(BitOR($UHID_MB, $UHID_BIOS))
	
	$sUniqueHwID = StringTrimLeft($sUniqueHwID, 1)
	$sUniqueHwID = StringTrimRight($sUniqueHwID, 1)
	$sUniqueHwID &= '-AERTFG' & StringLeft($sPRODUCT_ID, 3) & '-FGGHFGME' & StringTrimLeft($sPRODUCT_ID, 3)
	
	Return $sUniqueHwID
EndFunc

Func _ProductKey_Crypt($sPK)
	Local $s_PK = ''
	
	$s_PK &= StringRight($sPK, 4)
	$s_PK &= StringLeft($sPK, 4)
	$s_PK &= StringMid($sPK, 5, StringLen($sPK) - 8)
	
	Return StringReverse($s_PK)
EndFunc

Func _ProductKey_Delete($sKey)
	Local $iDel = IniDelete(@ScriptDir & '\Product.key', 'Data', $sKey)
	
	If @error Or $iDel == 0 Then
		RegDelete('HKLM\SOFTWARE\' & $sPRODUCT_NAME, $sKey)
	EndIf
	
	Return Number(@error = 0)
EndFunc

Func _ProductKey_Read($sKey)
	Local $sRead = IniRead(@ScriptDir & '\Product.key', 'Data', $sKey, '@ERROR@')
	
	If $sRead == '@ERROR@' Then
		$sRead = RegRead('HKLM\SOFTWARE\' & $sPRODUCT_NAME, $sKey)
	EndIf
	
	Return $sRead
EndFunc

Func _ProductKey_Write($sKey, $sVal)
	Local $iWrite = IniWrite(@ScriptDir & '\Product.key', 'Data', $sKey, $sVal)
	
	If @error Or $iWrite == 0 Then
		RegWrite('HKLM\SOFTWARE\' & $sPRODUCT_NAME, $sKey, 'REG_SZ', $sVal)
	EndIf
	
	Return Number(@error = 0)
EndFunc




Вариант по проще...
Создаёте на сервере php скрипт, который будет отправлять запрос на ваш email (где покупатель будет указывать квитанцию перевода средств от системы электронных денег и данные с вашего AutoIt-скрипта - данные о железе):

product_register.php
PHP:
<?php

$sApp = $_GET['app'];
$sImage = $_GET['img'];
$sLang = $_GET['lang'];
$sPrice = $_GET['price'];
$sPayment_Page = $_GET['ppage'];
$sEmail = $_GET['email'];
$sUNIQUEID = $_GET['uid'];

//Register (payment) form bellow

$sBody = 'Transfer #:%20%0D%0A%0D%0AEmail:%20%0D%0A%0D%0AUNIQUE ID: ' . $sUNIQUEID;

$sLng_Title = ($sLang == 'Russian' ? 'Покупка' : 'Buying');
$sLng_Price = ($sLang == 'Russian' ? 'Цена' : 'Price');
$sLng_Label1 = ($sLang == 'Russian' ? 'Пожалуйста, выполните соответствующую оплату на <a href="' . $sPayment_Page . '">странице покупки</a>, и <a href="mailto:' . $sEmail . '?subject=Product Key Request for ' . $sApp . '&body=' . $sBody . '">отправьте email</a> с номером/чеком перевода.' : 'Please make appropiate payment on the <a href="' . $sPayment_Page . '" target="_blank">payment page</a>, and <a href="mailto:' . $sEmail . '?subject=Product Key Request for ' . $sApp . '&body=' . $sBody . '">send email</a> with the transfer number/receipt.');
$sLng_Label2 = ($sLang == 'Russian' ? 'Пожалуйста, НЕ меняйте Тему и поле UNIQUE ID в теле письма' : 'Please do NOT change the subject and UNIQUE ID field in the body');
$sLng_Label3 = ($sLang == 'Russian' ? 'Просто добавьте номер перевода у поля "Transfer #:" или прикрепите к письму чек, а также укажите ваш email у поля "Email:"' : 'Just add transfer number to the "Transfer #:" field or attach receipt to the email, also enter your email to "Email:" field');
$sLng_Label4 = ($sLang == 'Russian' ? 'После подтверждения оплаты, Вы получите ключ продукта на указанный email' : 'When payment confirmed, you will recieve an email with your product key');
$sLng_Thanks = ($sLang == 'Russian' ? 'Спасибо что выбрали наш продукт :)' : 'Thank you for choosing our product :)');

echo '<body><title>' . $sLng_Title . ' ' .$sApp . '</title></body>';
echo '<h1>' . $sLng_Title . ' ' .$sApp . '</h1>
';
echo '<div style="text-align:center"><img src="' . $sImage . '" /></div>

';
echo '<b>' . $sLng_Price . ':</b> ' . $sPrice . '$
';
echo '<b>UNIQUE ID:</b> ' . $sUNIQUEID . '

';
echo $sLng_Label1 . '

';
echo '- <font style="color:red;font-weight:bold">' . $sLng_Label2 . '</font>
';
echo '- <font style="color:green;font-weight:bold">' . $sLng_Label3 . '</font>

';
echo '* <font style="color:gray;font-style:italic">' . $sLng_Label4 . '</font>



';
echo '<div style="text-align:center;font-weight:bold">' . $sLng_Thanks . '</div>
';

?>

И используете следующий AutoIt скрипт в качестве примера:

MyProg.au3
Код:
#include <WinAPIDiag.au3>
#include <APIDiagConstants.au3>

Global $sAPP_NAME			= 'My Program'
Global $sAPP_PRICE			= '10'
Global $sAPP_IMAGEURL		= 'http://myserver.ru/images/MyApp.jpg'
Global $sAPP_EMAIL			= '[email protected]'
Global $sPAYMENT_PAGE		= 'http://myserver.ru/payment-page.html'
Global $sREGISTER_URL		= 'http://myserver.ru/product_register.php'
Global $sPRODUCT_HWID		= _ProductKey_Read('hwid')
Global $fAPP_LICENSED 		= _ProductKey_Validate()

If $fAPP_LICENSED Then
	MsgBox(64, 'Title', 'Full version code')
	Exit
Else
	$sPage = StringFormat('?app=%s&img=%s&lang=%s&price=%s&ppage=%s&email=%s&uid=%s', _
		$sAPP_NAME, $sAPP_IMAGEURL, 'English', $sAPP_PRICE, $sPAYMENT_PAGE, $sAPP_EMAIL, _ProductKey_GetHardwareData())
	ShellExecute($sREGISTER_URL & $sPage)
	
	$sPK = InputBox('Product key', 'Please enter your Product Key:', '')
	
	If $sPK <> '' Then
		$sPRODUCT_HWID = $sPK
		$fAPP_LICENSED = _ProductKey_Validate()
	EndIf
EndIf

;.....

If $fAPP_LICENSED Then
	MsgBox(64, 'Title', 'Full version code (after registration)')
EndIf

Func _ProductKey_Validate()
	Local $sPK = _ProductKey_GenerateKey()
	
	If $sPRODUCT_HWID == $sPK Then
		_ProductKey_Write('hwid', $sPK)
		Return True
	EndIf
	
	Return False
EndFunc

Func _ProductKey_GenerateKey($sHWID = '')
	Local $sPK_Val = $sHWID
	
	If $sHWID = '' Then
		$sPK_Val = _ProductKey_GetHardwareData()
	EndIf
	
	Local $sRet_PK, $aSplit_PK = StringSplit(StringRegExpReplace(StringUpper($sPK_Val), "[^A-Z0-9]+", ""), "")
	
	For $i = $aSplit_PK[0] To 1 Step -2
		If Mod($i, 5) = 0 Then
			$sRet_PK &= $aSplit_PK[$i] & "-"
		Else
			$sRet_PK &= $aSplit_PK[$i]
		EndIf
	Next
	
	Return StringRegExpReplace($sRet_PK, "\A-+|-+$|\A.{1,4}-|-.{1,4}$", "")
EndFunc

Func _ProductKey_GetHardwareData()
	Local $sUniqueHwID = _WinAPI_UniqueHardwareID(BitOR($UHID_MB, $UHID_BIOS))
	Local $sName = StringStripWS($sAPP_NAME, 8)
	
	$sUniqueHwID = StringTrimLeft($sUniqueHwID, 1)
	$sUniqueHwID = StringTrimRight($sUniqueHwID, 1)
	$sUniqueHwID &= '-AERTFG' & StringLeft($sName, 3) & '-FGGHFGME' & StringTrimLeft($sName, 3)
	
	Return $sUniqueHwID
EndFunc

Func _ProductKey_Read($sKey)
	Local $sRead = IniRead(@ScriptDir & '\Product.key', 'Data', $sKey, '@ERROR@')
	
	If $sRead == '@ERROR@' Then
		$sRead = RegRead('HKLM\SOFTWARE\' & $sAPP_NAME, $sKey)
	EndIf
	
	Return $sRead
EndFunc

Func _ProductKey_Write($sKey, $sVal)
	Local $iWrite = IniWrite(@ScriptDir & '\Product.key', 'Data', $sKey, $sVal)
	
	If @error Or $iWrite == 0 Then
		RegWrite('HKLM\SOFTWARE\' & $sAPP_NAME, $sKey, 'REG_SZ', $sVal)
	EndIf
	
	Return Number(@error = 0)
EndFunc


* После получения данных на email, используем UNIQUE ID для генерации ключа продукта покупателя (используя _ProductKey_GenerateKey() из скрипта выше), и высылаем покупателю его ключ, который он введёт в соответствующее поле.
* Данный метод не предотвращает множественное использование оплаченного чека.



Ссылки по материалу:
Как продать свою программу
предпродажная подготовка
Теория защиты программ от взлома
Как грамотно продавать интеллектуальные продукты?
Система защиты и привязка к железу
Привязка скрипта к одному компьютеру (железу) (теория в этой же теме)
_UniqueHardwaeIDv1() - Генерация уникального ID компьютера
 

firex

AutoIT Гуру
Сообщения
943
Репутация
208
CreatoR
Ах вот оно что, а я думал вы свой проект развивали :smile:
Хотелось бы узнать - почему остановились на digiseller, а не на plati?

С digiseller, к сожалению, не знаком. На первый взгляд он менее раскручен / известен.
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
firex [?]
Хотелось бы узнать - почему остановились на digiseller, а не на plati?
А разве это не одно и то же?
При регистрации меня перенаправило на digiseller, вот я его и взял за основу :smile:.
 

firex

AutoIT Гуру
Сообщения
943
Репутация
208
CreatoR
Из головы вылетело за время, видимо, стоит быть более внимательным.
 

dimcomp

Новичок
Сообщения
66
Репутация
0
Спасибо за тему! Жаль что только недавно решил сюда заглянуть. :smile: Но есть ряд вопросов.
1. Для какой версии AutoIt написан пример программы? У меня стоит 3.3.8.1 пришлось инклюды переписывать. После успешной тестовой покупки и вводом ключа вылезла ошибка в строке 224 Return StringReverse($s_PK) скорей всего из-за старой версии?
2. Я так понимаю что ключ даётся на всё время, а можно ли доработать чтобы ключ действовал неделю, месяц, год?
3. Если получится прикрутить временные ключи, то можно ли сделать так, чтобы если вдруг покупатель захочет продлить программу и купить продление ключа заранее, чтобы остаток времени прибавлялся к новому ключу?
 

cnm

Новичок
Сообщения
58
Репутация
2
Ну что сказать - на 2015 год инструкция - полный отстой, не имеет защиты от банальной переадресации запросов.
а за два прошедших года можно было уж создать алгоритмы помощнее с сорцами.

Прикрепление статьи в топе позорит Creator'a как автора.
 

joiner

Модератор
Локальный модератор
Сообщения
3,556
Репутация
628
cnm
ждем с нетерпением твоего "толкового" материала по теме
 

filautdinov

Знающий
Сообщения
96
Репутация
9
cnm сказал(а):
Ну что сказать - на 2015 год инструкция - полный отстой, не имеет защиты от банальной переадресации запросов.
а за два прошедших года можно было уж создать алгоритмы помощнее с сорцами.

Прикрепление статьи в топе позорит Creator'a как автора.
Это если ты знаешь какие запросы принимает и отдает сервер, пример у меня написан софт и свой сервер не на autoit и запросы и в ту и другую стороны шифрованы с уникальным алго шифрованием посему твоя переадресация тупо ни чего не даст, так как каждый запрос будет отличаться от предыдущего.
 

cnm

Новичок
Сообщения
58
Репутация
2
filautdinov сказал(а):
Это если ты знаешь какие запросы принимает и отдает сервер, пример у меня написан софт и свой сервер не на autoit и запросы и в ту и другую стороны шифрованы с уникальным алго шифрованием посему твоя переадресация тупо ни чего не даст, так как каждый запрос будет отличаться от предыдущего.

О чем и речь. Creator такую мелочь не продумал.
 

Атос

Новичок
Сообщения
85
Репутация
0
cnm - ты дерьмо позорное, а не Creator позорится, как автор.
Он для продвижения AutoIt-а в России сделал больше, чем ты для своих родителей.
Короче, cnm - ты дерьмо собачье.
 

Garrett

Модератор
Локальный модератор
Сообщения
3,999
Репутация
967
Атос
В следующий раз будет отдых на три дня :ninja2:
Контролируйте свои эмоции пожалуйста.

cnm [?]
инструкция - полный отстой
Я так понимаю, ваше жизненное кредо всё или ничего! Пока, лично я, ничего не вижу.
 
Верх