Принципи генерації промокодів

Дані користувача

Email

Омніканальність

Автоматизація

Принципи генерації промокодів за допомогою PHP/JAVA

Якщо загальні методи підстановки промокодів вам не підходять, розгляньте можливість реалізації цього завдання за допомогою PHP/JAVA.

Формат промокоду, який підтримує eSputnik

Промокод містить дату, до якої він діє, тип акції, розмір знижки та контрольну суму.

У результаті отримаємо такий рядок:

<YY><MM><DD><Promo type><Discount><CRC>
Поле Довжина Опис Приклад
YY 2 символи Рік, двозначне число 15, 54
MM 2 символи Місяць, двозначне число, якщо треба, доповнюється зліва нулем 01, 12
DD 2 символи День, двозначне число, якщо треба, доповнюється зліва нулем 06, 28
Promo type 1 символ Тип промокоду, може набувати значення від 0 до 31, кодується символом з алфавіту Base32 A, D, X
Discount 2 символи Розмір знижки у %, двозначне число, якщо треба, доповнюється нулем зліва 15, 02
CRC 1 символ Контрольна сума – одне зі значень алфавіту Base32 B, N, Z

У результаті виходить рядок завдовжки 10 символів такого виду: 151231Y16N

Зверніть увагу

Значення промокодів залежить від параметрів блоку та поточного календарного числа. Тобто протягом доби всі контакти отримуватимуть однаковий промокод, якщо у параметри блоку не вноситимуться зміни. Відстеження використання промокоду та терміну його дії має відбуватись на вашому боці.

Приклад використання типу промокоду (Promo type)

  • 0 – діє для всього кошика (must-have)
  • 1 – діє лише для категорії А
  • 2 – діє лише для категорії В

Важливо!

Якщо ви хочете використовувати типи промокоду таким чином, надішліть файл у підтримку eSputnik, вказавши, яка категорія продукту відповідає тому чи іншому коду.

Алгоритм розрахунку контрольної суми (CRC)

  1. По порядку сумується десяткове ASCII-значення кожного символу у вихідному рядку.
  2. Отримана сума ділиться із залишком на 32.
  3. Отриманий залишок використовується як індекс символу в алфавіті Base32.

Символ цього індекса і буде контрольною сумою.

Приклад коду на java:

public static final String BASE32_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";

public char checkSum(String data) {
    int sum = 0;
    for (char c : data.toCharArray()) {
        sum += c;
}
    return BASE32_ALPHABET.charAt(sum % BASE32_ALPHABET.length()); 
}

Кодування/декодування промокоду

Для кодування/декодування використовується симетричний алгоритм шифрування Triple DES.

Щоб отримати людиночитаний текст, отримані під час шифрування дані кодуються за допомогою Base32.

Параметри шифрування, що використовуються:

  • ключ довжиною 24 байти,
  • initialization vector довжиною 8 байтів,
  • режим шифрування: CFB8 (Cipher Feedback Mode),
  • доповнення: NoPadding.

Послідовність дій під час кодування промокоду

  1. До промокоду застосовується алгоритм шифрування Triple DES.
  2. Щоб отримати людиночитаний текст, отримані за допомогою шифрування дані кодуються за допомогою Base32.
  3. Через кожні 4 символи для читабельності додається “-”.

Послідовність дій при декодуванні промокоду

  1. Видаліть “-”.
  2. Спочатку дані декодуються за допомогою Base32.
  3. Далі декодовані дані розшифровуються за допомогою Triple DES.
  4. Має вийти рядок із 10 символів згідно з описаним вище форматом. Для контролю правильності розшифрування використовується контрольна сума.

Секретний ключ

Для кодування та декодування промокодів секретний ключ використовується на стороні eSputnik і на стороні сервісу, який перевіряє промокоди.

Згенеруйте ключ eSSuperKeyXXXXXXXXXXXXXX (xxx… – випадкові цифри). Придумайте вектор ініціалізації у такому форматі: 12345678.

Передайте обидва ці значення в підтримку eSputnik.

Приклад Java-коду

package promocode;

import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;

import org.apache.commons.codec.binary.Base32;

public class CryptData {
    private KeySpec keySpec;
    private SecretKey key;
    private IvParameterSpec iv;

    public CryptData(String keyString, String ivString) {
        try {
            keySpec = new DESedeKeySpec(keyString.getBytes("UTF-8"));

            key = SecretKeyFactory.getInstance("DESede")
                    .generateSecret(keySpec);

            iv = new IvParameterSpec(ivString.getBytes("UTF-8"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String encrypt(String value) {
        try {
            Cipher ecipher = Cipher.getInstance("DESede/CFB8/NoPadding");

            // "SunJCE");
            ecipher.init(Cipher.ENCRYPT_MODE, key, iv);

            if (value == null)
                return null;

            // Encode the string into bytes using utf-8
            byte[] valeur = value.getBytes("UTF-8");

            // Encrypt
            byte[] enc = ecipher.doFinal(valeur);

            // Encode bytes to base64 to get a string
            String encodedString = new String(new Base32().encode(enc), "UTF-8");
            StringBuilder sb = new StringBuilder();
            for (int i = 0 ; i < encodedString.length(); ++i) {
                if (0 != i && i % 4 == 0) {
                    sb.append("-");
                }
                sb.append(encodedString.charAt(i));
            }
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    public static void main(String[] args) {
        String encrypt = new CryptData("eSSuperKeyXXXXXXXXXXXXXX", "12345678").encrypt("770101K99N");
        System.err.println(encrypt);
    }
}

Приклад PHP-коду

 'A',
        1 => 'B',
        2 => 'C',
        3 => 'D',
        4 => 'E',
        5 => 'F',
        6 => 'G',
        7 => 'H',
        8 => 'I',
        9 => 'J',
        10 => 'K',
        11 => 'L',
        12 => 'M',
        13 => 'N',
        14 => 'O',
        15 => 'P',
        16 => 'Q',
        17 => 'R',
        18 => 'S',
        19 => 'T',
        20 => 'U',
        21 => 'V',
        22 => 'W',
        23 => 'X',
        24 => 'Y',
        25 => 'Z',
        26 => 2,
        27 => 3,
        28 => 4,
        29 => 5,
        30 => 6,
        31 => 7,
        32 => '=',
    );
    /**
     * Table for decoding base32
     *
     * @var array
     */
    private static $decode = array(
        'A' => 0,
        'B' => 1,
        'C' => 2,
        'D' => 3,
        'E' => 4,
        'F' => 5,
        'G' => 6,
        'H' => 7,
        'I' => 8,
        'J' => 9,
        'K' => 10,
        'L' => 11,
        'M' => 12,
        'N' => 13,
        'O' => 14,
        'P' => 15,
        'Q' => 16,
        'R' => 17,
        'S' => 18,
        'T' => 19,
        'U' => 20,
        'V' => 21,
        'W' => 22,
        'X' => 23,
        'Y' => 24,
        'Z' => 25,
        2 => 26,
        3 => 27,
        4 => 28,
        5 => 29,
        6 => 30,
        7 => 31,
        '=' => 32,
    );
    /**
     * Creates an array from a binary string into a given chunk size
     *
     * @param string $binaryString String to chunk
     * @param integer $bits Number of bits per chunk
     * @return array
     */
    private static function chunk($binaryString, $bits)
    {
        $binaryString = chunk_split($binaryString, $bits, ' ');
        if (substr($binaryString, (strlen($binaryString)) - 1)  == ' ') {
            $binaryString = substr($binaryString, 0, strlen($binaryString)-1);
        }
        return explode(' ', $binaryString);
    }
    /**
     * Encodes into base32
     *
     * @param string $string Clear text string
     * @return string Base32 encoded string
     */
    public static function encode($string)
    {
        if (strlen($string) == 0) {
            // Gives an empty string
            return '';
        }
        // Convert string to binary
        $binaryString = '';
        foreach (str_split($string) as $s) {
            // Return each character as an 8-bit binary string
            $s = decbin(ord($s));
            $binaryString .= str_pad($s, 8, 0, STR_PAD_LEFT);
        }
        // Break into 5-bit chunks, then break that into an array
        $binaryArray = self::chunk($binaryString, 5);
        // Pad array to be divisible by 8
        while (count($binaryArray) % 8 !== 0) {
            $binaryArray[] = null;
        }
        $base32String = '';
        // Encode in base32
        foreach ($binaryArray as $bin) {
            $char = 32;
            if (!is_null($bin)) {
                // Pad the binary strings
                $bin = str_pad($bin, 5, 0, STR_PAD_RIGHT);
                $char = bindec($bin);
            }
            // Base32 character
            $base32String .= self::$encode[$char];
        }
        return $base32String;
    }
    /**
     * Decodes base32
     *
     * @param string $base32String Base32 encoded string
     * @return string Clear text string
     */
    public static function decode($base32String)
    {
        if (strlen($base32String) == 0) {
            // Gives an empty string
            return '';
        }
        // Only work in upper cases
        $base32String = strtoupper($base32String);
        // Remove anything that is not base32 alphabet
        $pattern = '/[^A-Z2-7]/';
        $base32String = preg_replace($pattern, '', $base32String);
        $base32Array = str_split($base32String);
        $string = '';
        foreach ($base32Array as $str) {
            $char = self::$decode[$str];
            // Ignore the padding character
            if ($char !== 32) {
                $char = decbin($char);
                $string .= str_pad($char, 5, 0, STR_PAD_LEFT);
            }
        }
        while (strlen($string) %8 !== 0) {
            $string = substr($string, 0, strlen($string)-1);
        }
        $binaryArray = self::chunk($string, 8);
        $realString = '';
        foreach ($binaryArray as $bin) {
            // Pad each value to 8 bits
            $bin = str_pad($bin, 8, 0, STR_PAD_RIGHT);
            // Convert binary strings to ASCII
            $realString .= chr(bindec($bin));
        }
        return $realString;
    }
}

    $base32 = new Base32;
    $key = "eSSuperKeyXXXXXXXXXXXXXX";
    $iv = "12345678";
    $encoded = "ILDD-2V7W-SBWD-2X34";

    echo "DECODED: " . mcrypt_decrypt(MCRYPT_3DES, $key, $base32::decode(str_replace("-", "", $encoded)), MCRYPT_MODE_CFB, $iv) . "\n";
?>

Важливо!

Використовуйте Format Preserving Encryption, щоб спростити переведення коду в людиночитаний формат.

Залишилися питання?
Спеціалісти обов'язково нададуть відповідь та допоможуть вирішити вашу проблему!
Зворотний дзвінок
Залишіть заявку – і наш спеціаліст зв'яжеться з вами в робочий час.
Відправити заявку
Консультація в чаті
Готові до ваших запитань!
Написати в чат
Електронна пошта
Напишіть в службу підтримки eSputnik.
Надіслати email