walletone 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +0 -0
- data/README.md +110 -0
- data/lib/walletone.rb +52 -0
- data/lib/walletone/errors/bad_url_error.rb +4 -0
- data/lib/walletone/errors/walletone_error.rb +4 -0
- data/lib/walletone/fields.rb +81 -0
- data/lib/walletone/form.rb +30 -0
- data/lib/walletone/middleware.rb +5 -0
- data/lib/walletone/middleware/base.rb +68 -0
- data/lib/walletone/middleware/callback.rb +19 -0
- data/lib/walletone/notification.rb +17 -0
- data/lib/walletone/payment.rb +44 -0
- data/lib/walletone/signer.rb +45 -0
- data/lib/walletone/version.rb +3 -0
- data/spec/fabricators/fields_fabricator.rb +6 -0
- data/spec/fabricators/payment_fabricator.rb +5 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/walletone/fields_spec.rb +54 -0
- data/spec/walletone/form_spec.rb +20 -0
- data/spec/walletone/middleware/base_spec.rb +22 -0
- data/spec/walletone/middleware/callback_spec.rb +14 -0
- data/spec/walletone/notification_spec.rb +36 -0
- data/spec/walletone/payment_spec.rb +60 -0
- data/spec/walletone/signer_spec.rb +42 -0
- data/spec/walletone_spec.rb +18 -0
- data/walletone.gemspec +35 -0
- metadata +266 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: ab9a944684b8e39c9412cb122c2fd1d7b128a06f
         | 
| 4 | 
            +
              data.tar.gz: 9ed98422688067832bed0ca481d1302e80237e47
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: a9bf93be1b557b034411849dcdaeceb4280b19a9c75f87c3d57cc85913456ae2528b42d0b6b82837f10e80563e9c34bee779f597b60f0b96587672742d82d16b
         | 
| 7 | 
            +
              data.tar.gz: 39f6840032fac8b8fbfc0ab3856338bce8c2095ae59fa592e3a4462715416b53efd456303d6efa1da72495463211c4791fe4b65efdd775a8420069e091552361
         | 
    
        data/LICENSE
    ADDED
    
    | 
            File without changes
         | 
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,110 @@ | |
| 1 | 
            +
            # walletone checkout client for ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            [](https://travis-ci.org/BrandyMint/walletone)
         | 
| 4 | 
            +
            [](https://codeclimate.com/github/BrandyMint/walletone)
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            ---
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            ## Прием уведомлений об оплате (rack middleware)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            ### 1. Создаем rack middleware для приема уведомлений
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            Сначала определимся как мы будем получать уведомление об оплате.
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            #### Вариант N1. Через callback
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            ```ruby
         | 
| 17 | 
            +
            wm = Walletone::Middleware::Callback.new do |notify, env|
         | 
| 18 | 
            +
              # notify is a Walletone::Notification instance
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              raise 'Wrong sign' unless notify.valid? W1_SECRET_KEY
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              # TODO something with notify
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              'Return some message for OK response'
         | 
| 25 | 
            +
            end
         | 
| 26 | 
            +
            ```
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            #### Вариант N2. Создаем свой класс middleware
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            ```ruby
         | 
| 31 | 
            +
            class MyApp::WalletoneMiddleware < Waletone::Middleware::Base
         | 
| 32 | 
            +
              def perform notify, env
         | 
| 33 | 
            +
                raise 'Wrong sign' unless notify.valid? W1_SECRET_KEY
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                # TODO something with notify
         | 
| 36 | 
            +
                'Return some message for OK response'
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
            end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            wm = MyApp::WalletoneMiddleware.new
         | 
| 41 | 
            +
            ```
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            ### 2. Подключаем middleware
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            ```ruby
         | 
| 46 | 
            +
            # config.ru
         | 
| 47 | 
            +
            run wm
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            # rails
         | 
| 50 | 
            +
            mount wm => '/w1_callback'
         | 
| 51 | 
            +
            ```
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            ### 3. Прописываем в настройках мерчанта на walletone.com получившийся url
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            Готово. Принимаем уведомления.
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            ## Генерация формы оплаты
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            ### Сначала заполняем необходимые для формы поля
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            Поля хранятся в объекте типа `Walletone::Payment`, который представляет из 
         | 
| 62 | 
            +
            себя `Hash` с возможностью прямого доступа к основным полям с префиксом `WMI_`.
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            Для полей требующих множественное значение (типа `WMI_PTENABLED`) в качестве
         | 
| 65 | 
            +
            значений можно передавать массив строк.
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            ```ruby
         | 
| 68 | 
            +
            payment = Walletone::Payment.new(
         | 
| 69 | 
            +
                WMI_MERCHANT_ID:    'Номер вашего merchant ID',
         | 
| 70 | 
            +
                WMI_PAYMENT_AMOUNT:  10000, # Сумма
         | 
| 71 | 
            +
                WMI_CURRENCY_ID:     643, # ISO номер валюты (По умолчанию 643 - Рубль),
         | 
| 72 | 
            +
                WMI_PTENABLED:      ['WebMoneyRUB', 'WebMoneyUSD'],
         | 
| 73 | 
            +
                SOME_CUSTOM_FIELD:  'value'
         | 
| 74 | 
            +
                # etc любые другие поля
         | 
| 75 | 
            +
            )
         | 
| 76 | 
            +
            ```
         | 
| 77 | 
            +
             | 
| 78 | 
            +
             | 
| 79 | 
            +
            ### Собственно генераця формы
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            ```haml
         | 
| 82 | 
            +
            - form = Walletone::Form.new payment
         | 
| 83 | 
            +
             | 
| 84 | 
            +
            %h5 В течение 5-и секунд вы будете переправлены на страницу оплаты.
         | 
| 85 | 
            +
            = form_tag form.checkout_url, form.options.merge(data: {autosubmit: true}) do |f|
         | 
| 86 | 
            +
              = form.hidden_fields_tags
         | 
| 87 | 
            +
              = submit_tag 'Перейти к оплате', 
         | 
| 88 | 
            +
                :class=>'btn btn-primary',
         | 
| 89 | 
            +
                :data =>{:disable_with => 'Переправляю на сайт оплаты..'}
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            :coffee
         | 
| 92 | 
            +
              # Автоматически сабмитим форму, чтобы не тревожить лишний раз
         | 
| 93 | 
            +
              # пользователя
         | 
| 94 | 
            +
              $ -> $('[data-autosubmit]').submit()
         | 
| 95 | 
            +
            ```
         | 
| 96 | 
            +
             | 
| 97 | 
            +
            # Прочие ссылки
         | 
| 98 | 
            +
             | 
| 99 | 
            +
            1. [Walletone Open API](https://api.w1.ru/OpenApi/)
         | 
| 100 | 
            +
            2. [Документация (API)](http://www.walletone.com/ru/merchant/documentation/)
         | 
| 101 | 
            +
             | 
| 102 | 
            +
            ---
         | 
| 103 | 
            +
             | 
| 104 | 
            +
            ### Contributing
         | 
| 105 | 
            +
             | 
| 106 | 
            +
            1. Fork it
         | 
| 107 | 
            +
            2. Create your feature branch (`git checkout -b my-new-feature`)
         | 
| 108 | 
            +
            3. Commit your changes (`git commit -am 'add some feature'`)
         | 
| 109 | 
            +
            4. Push to the branch (`git push origin my-new-feature`)
         | 
| 110 | 
            +
            5. Create new Pull Request
         | 
    
        data/lib/walletone.rb
    ADDED
    
    | @@ -0,0 +1,52 @@ | |
| 1 | 
            +
            require 'logger'
         | 
| 2 | 
            +
            require 'virtus'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'walletone/errors/walletone_error'
         | 
| 5 | 
            +
            require 'walletone/errors/bad_url_error'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            require 'walletone/payment'
         | 
| 8 | 
            +
            require 'walletone/notification'
         | 
| 9 | 
            +
            require 'walletone/middleware/base'
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            module Walletone
         | 
| 12 | 
            +
              API_URL          = 'https://api.w1.ru/OpenApi/'
         | 
| 13 | 
            +
              V1_CHECKOUT_URL = 'https://www.walletone.com/checkout/default.aspx'
         | 
| 14 | 
            +
              V2_CHECKOUT_URL = 'https://wl.walletone.com/checkout/checkout/Index'
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              class Configuration
         | 
| 17 | 
            +
                include ::Singleton
         | 
| 18 | 
            +
                attr_accessor :logger, :error_notifier, :error_notify_method, :web_checkout_url
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def self.default_logger
         | 
| 21 | 
            +
                  logger = Logger.new(STDOUT)
         | 
| 22 | 
            +
                  logger.progname = 'walletone'
         | 
| 23 | 
            +
                  logger
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def initialize
         | 
| 27 | 
            +
                  self.logger = self.class.default_logger
         | 
| 28 | 
            +
                  self.web_checkout_url = V2_CHECKOUT_URL
         | 
| 29 | 
            +
                  self.error_notify_method = :notify
         | 
| 30 | 
            +
                  self.error_notifier = Honeybadger if defined? Honeybadger
         | 
| 31 | 
            +
                  self.error_notifier = Bugsnag if defined? Bugsnag
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              def self.notify_error error, *args
         | 
| 36 | 
            +
                logger.error "Catch error #{error}"
         | 
| 37 | 
            +
                return unless config.error_notifier
         | 
| 38 | 
            +
                config.error_notifier.send self.config.error_notify_method, error, *args
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              def self.config
         | 
| 42 | 
            +
                Configuration.instance
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              def self.configure
         | 
| 46 | 
            +
                yield config
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              def self.logger
         | 
| 50 | 
            +
                config.logger
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
            end
         | 
| @@ -0,0 +1,81 @@ | |
| 1 | 
            +
            # Базовый класс для полей формы и уведомления
         | 
| 2 | 
            +
            #
         | 
| 3 | 
            +
            module Walletone
         | 
| 4 | 
            +
              class Fields < Hash
         | 
| 5 | 
            +
                # http://www.walletone.com/ru/merchant/documentation/#step2
         | 
| 6 | 
            +
                #
         | 
| 7 | 
            +
                #
         | 
| 8 | 
            +
                W1_KEYS = %i(
         | 
| 9 | 
            +
                    WMI_MERCHANT_ID
         | 
| 10 | 
            +
                    WMI_PAYMENT_AMOUNT
         | 
| 11 | 
            +
                    WMI_CURRENCY_ID
         | 
| 12 | 
            +
                    WMI_PAYMENT_NO
         | 
| 13 | 
            +
                    WMI_DESCRIPTION
         | 
| 14 | 
            +
                    WMI_SUCCESS_URL WMI_FAIL_URL
         | 
| 15 | 
            +
                    WMI_EXPIRED_DATE
         | 
| 16 | 
            +
                    WMI_PTENABLED WMI_PTDISABLED
         | 
| 17 | 
            +
                    WMI_RECIPIENT_LOGIN
         | 
| 18 | 
            +
                    WMI_CUSTOMER_FIRSTNAME WMI_CUSTOMER_LASTNAME WMI_CUSTOMER_EMAIL
         | 
| 19 | 
            +
                    WMI_CULTURE_ID
         | 
| 20 | 
            +
                    WMI_SIGNATURE
         | 
| 21 | 
            +
                    WMI_DELIVERY_REQUEST 
         | 
| 22 | 
            +
                    WMI_DELIVERY_COUNTRY WMI_DELIVERY_REGION WMI_DELIVERY_CITY WMI_DELIVERY_ADDRESS
         | 
| 23 | 
            +
                    WMI_DELIVERY_CONTACTINFO WMI_DELIVERY_COMMENTS WMI_DELIVERY_ORDERID 
         | 
| 24 | 
            +
                    WMI_DELIVERY_DATEFROM WMI_DELIVERY_DATETILL
         | 
| 25 | 
            +
                    WMI_PSP_MERCHANT_ID
         | 
| 26 | 
            +
                )
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                MULTIPLE_VALUES = %i(WMI_PTENABLED WMI_PTDISABLED)
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                # Определяем методы для прямого доступа
         | 
| 31 | 
            +
                # > payment.WMI_MERCHANT_ID
         | 
| 32 | 
            +
                # > payment.WMI_MERCHANT_ID=123
         | 
| 33 | 
            +
                #
         | 
| 34 | 
            +
                # > payment.WMI_PTENABLED = 
         | 
| 35 | 
            +
                W1_KEYS.each do |k|
         | 
| 36 | 
            +
                  define_method k do
         | 
| 37 | 
            +
                    fetch k
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  define_method "#{k}=" do |value|
         | 
| 41 | 
            +
                    self[k]= value
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def initialize attrs={}
         | 
| 46 | 
            +
                  fields = super
         | 
| 47 | 
            +
                  return fields if attrs.empty?
         | 
| 48 | 
            +
                  symbolyzed_attrs = attrs.inject({}) { |acc, e| acc[e.first.to_sym]=e.last; acc }
         | 
| 49 | 
            +
                  fields.merge! symbolyzed_attrs
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def [] key
         | 
| 53 | 
            +
                  fetch key.to_sym, nil
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                def []= key, value
         | 
| 57 | 
            +
                  super key.to_sym, value
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                def fetch key, default=nil
         | 
| 61 | 
            +
                  super key.to_sym, default
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                def as_list
         | 
| 65 | 
            +
                  keys.inject([]) do |acc, name|
         | 
| 66 | 
            +
                    # Делаем именно так, чтобы работало переопределение методов
         | 
| 67 | 
            +
                    # в Payment
         | 
| 68 | 
            +
                    value = respond_to?(name) ? send(name) : fetch(name)
         | 
| 69 | 
            +
                    Array(value).each { |v| acc << [name.to_s, v.to_s] }
         | 
| 70 | 
            +
                    acc
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                private
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                def signer
         | 
| 77 | 
            +
                  Signer.new fields: self
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
              end
         | 
| 81 | 
            +
            end
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            require 'cgi'
         | 
| 2 | 
            +
            module Walletone
         | 
| 3 | 
            +
              class Form
         | 
| 4 | 
            +
                include Virtus.model strict: true, coerce: false
         | 
| 5 | 
            +
                attribute :payment, Payment, required: true
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def checkout_url
         | 
| 8 | 
            +
                  Walletone.config.web_checkout_url
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def hidden_fields_tags
         | 
| 12 | 
            +
                  payment.as_list.map do |field|
         | 
| 13 | 
            +
                    key, value = field
         | 
| 14 | 
            +
                    hidden_field_tag(key, value)
         | 
| 15 | 
            +
                  end.join
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                # Рекомендуемые опции
         | 
| 19 | 
            +
                def options
         | 
| 20 | 
            +
                  { authenticity_token: false, enforce_utf8: false }
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                private
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def hidden_field_tag name, value
         | 
| 26 | 
            +
                  "<input name=\"#{CGI.escapeHTML(name.to_s)}\" type=\"hidden\" value=\"#{CGI.escapeHTML(value.to_s)}\" />"
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
            end
         | 
| @@ -0,0 +1,68 @@ | |
| 1 | 
            +
            require 'rack'
         | 
| 2 | 
            +
            require 'walletone/notification'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            # Это rack-middleware который принимает на себя
         | 
| 5 | 
            +
            # первый удар с уведомлением от walletone
         | 
| 6 | 
            +
            # Несет следующие обязанности:
         | 
| 7 | 
            +
            #
         | 
| 8 | 
            +
            # 1. Перевести параметры из cp1251 в unicode
         | 
| 9 | 
            +
            # 2. Построить объект типа Noficiation
         | 
| 10 | 
            +
            # 3. Вызвать метод perform с аргументами notify и env
         | 
| 11 | 
            +
            #
         | 
| 12 | 
            +
            # Метод peform должет быть переопределен в классах наследниках.
         | 
| 13 | 
            +
            # Например Walletone::Middleware::Base
         | 
| 14 | 
            +
            #
         | 
| 15 | 
            +
            module Walletone::Middleware
         | 
| 16 | 
            +
              class Base
         | 
| 17 | 
            +
                OK = 'OK'
         | 
| 18 | 
            +
                RETRY = 'RETRY'
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def call(env)
         | 
| 21 | 
            +
                  logger.info 'Middleware start'
         | 
| 22 | 
            +
                  request  = Rack::Request.new env
         | 
| 23 | 
            +
                  encoded_params = from_cp1251_to_utf8 request.params
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  logger.info "Middleware parameters is #{encoded_params}"
         | 
| 26 | 
            +
                  notify = Walletone::Notification.new encoded_params
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  logger.info 'Middleware perform'
         | 
| 29 | 
            +
                  ok_message = perform notify, env
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  logger.info "Middleware result is #{ok_message}'"
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  body = make_response OK, ok_message
         | 
| 34 | 
            +
                  [200, {}, [body]]
         | 
| 35 | 
            +
                rescue => err
         | 
| 36 | 
            +
                  Walletone.notify_error err
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  [200, {}, [make_response(RETRY, err.message)]]
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                private
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                def logger
         | 
| 44 | 
            +
                  Walletone.logger
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                def perform notify, env
         | 
| 48 | 
            +
                  raise 'Walletone middleware perform method is not implemented'
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                def make_response(result, description)
         | 
| 52 | 
            +
                  # NOTE А нужно ли тут конвертировать в cp1251?
         | 
| 53 | 
            +
                  if description
         | 
| 54 | 
            +
                    "WMI_RESULT=#{result}&WMI_DESCRIPTION=#{description}"
         | 
| 55 | 
            +
                  else
         | 
| 56 | 
            +
                    "WMI_RESULT=#{result}"
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                # walletone передает все параметры в cp1251
         | 
| 61 | 
            +
                # приводим в uft-8
         | 
| 62 | 
            +
                def from_cp1251_to_utf8(params)
         | 
| 63 | 
            +
                  params.map do |k, v|
         | 
| 64 | 
            +
                    [k, v.force_encoding("cp1251").encode("utf-8", undef: :replace)]
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
            end
         | 
| @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            module Walletone::Middleware
         | 
| 2 | 
            +
              class Callback < Base
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                def initialize callback
         | 
| 5 | 
            +
                  raise 'Callback must be a Proc' unless callback.is_a? Proc
         | 
| 6 | 
            +
                  @callback = callback
         | 
| 7 | 
            +
                  super()
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                private
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                attr_reader :callback
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def perform notify, env
         | 
| 15 | 
            +
                  callback.call notify, env
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
            end
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            require 'walletone/fields'
         | 
| 2 | 
            +
            # Уведомление от walletone которое пришло через rack middleware callback
         | 
| 3 | 
            +
            #
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Walletone
         | 
| 6 | 
            +
              class Notification < Fields
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def initialize params
         | 
| 9 | 
            +
                  super( params ).freeze
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def valid?(secret_key, hash_type = Signer::DEFAULT_HASH_TYPE)
         | 
| 13 | 
            +
                  self.WMI_SIGNATURE == signer.signature( secret_key, hash_type )
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
            end
         | 
| @@ -0,0 +1,44 @@ | |
| 1 | 
            +
            require 'walletone/fields'
         | 
| 2 | 
            +
            require 'walletone/signer'
         | 
| 3 | 
            +
            require 'base64'
         | 
| 4 | 
            +
            require 'time'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            # Поля для формы оплаты формируемые приложением
         | 
| 7 | 
            +
            #
         | 
| 8 | 
            +
            module Walletone
         | 
| 9 | 
            +
              class Payment < Fields
         | 
| 10 | 
            +
                RUB_ISO_NUMBER = 643
         | 
| 11 | 
            +
                DEFAULT_CURRENTY = RUB_ISO_NUMBER
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def sign!(secret_key, hash_type=Signer::DEFAULT_HASH_TYPE)
         | 
| 14 | 
            +
                  raise 'Already signed!' if signed?
         | 
| 15 | 
            +
                  self.WMI_SIGNATURE = signer.signature secret_key, hash_type
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  freeze
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def signed?
         | 
| 21 | 
            +
                  self.WMI_SIGNATURE.is_a?(String) && ! self.WMI_SIGNATURE.empty?
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def WMI_CURRENCY_ID
         | 
| 25 | 
            +
                  super || DEFAULT_CURRENTY
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def WMI_PAYMENT_AMOUNT
         | 
| 29 | 
            +
                  value = super
         | 
| 30 | 
            +
                  "%.2f" % value if value
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def WMI_DESCRIPTION
         | 
| 34 | 
            +
                  value = super
         | 
| 35 | 
            +
                  "BASE64:#{Base64.encode64(value[0...250])[0..-2]}" if value
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def WMI_EXPIRED_DATE
         | 
| 39 | 
            +
                  value = super
         | 
| 40 | 
            +
                  value ? value.iso8601 : value
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
            end
         | 
| @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            require 'digest/sha1'
         | 
| 2 | 
            +
            require 'digest/md5'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'walletone/fields'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Walletone
         | 
| 7 | 
            +
              class Signer
         | 
| 8 | 
            +
                include Virtus.model strict: true, coerce: false
         | 
| 9 | 
            +
                SIGN_HASH_TYPES   = [:md5, :sha1]
         | 
| 10 | 
            +
                DEFAULT_HASH_TYPE = :md5
         | 
| 11 | 
            +
                WMI_SIGNATURE     = 'WMI_SIGNATURE'
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                attribute :fields,     Walletone::Fields, requried: true
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def signature secret_key, hash_type=DEFAULT_HASH_TYPE
         | 
| 16 | 
            +
                  case hash_type
         | 
| 17 | 
            +
                  when :md5
         | 
| 18 | 
            +
                    Digest::MD5.base64digest( fields_as_string( secret_key ) )
         | 
| 19 | 
            +
                  when :sha1
         | 
| 20 | 
            +
                    Digest::SHA1.base64digest( fields_as_string( secret_key ) )
         | 
| 21 | 
            +
                  else
         | 
| 22 | 
            +
                    raise ArgumentError, hash_type
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                private
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def fields_as_string secret_key
         | 
| 29 | 
            +
                  [sorted_values, secret_key].join.encode('cp1251')
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                # Удаляем подпись и сортируем поля в алфавитном порядке
         | 
| 33 | 
            +
                #
         | 
| 34 | 
            +
                def sorted_values
         | 
| 35 | 
            +
                  fields
         | 
| 36 | 
            +
                    .as_list
         | 
| 37 | 
            +
                    .reject { |k, v| k == WMI_SIGNATURE }
         | 
| 38 | 
            +
                    .sort { |a, b|  [ a[0], a[1] ] <=> [ b[0], b[1] ] }
         | 
| 39 | 
            +
                    .map(&:last)
         | 
| 40 | 
            +
                    .map(&:to_s)
         | 
| 41 | 
            +
                    .compact
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
            end
         | 
    
        data/spec/spec_helper.rb
    ADDED
    
    | @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            require 'walletone'
         | 
| 2 | 
            +
            require 'fabrication'
         | 
| 3 | 
            +
            require 'ffaker'
         | 
| 4 | 
            +
            require 'pry'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Dir["./spec/support/**/*.rb"].sort.each { |f| require f}
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            RSpec.configure do |config|
         | 
| 9 | 
            +
              config.order = :random
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              config.before(:all) do
         | 
| 12 | 
            +
                Walletone.logger.level = Logger::FATAL
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
            end
         | 
| @@ -0,0 +1,54 @@ | |
| 1 | 
            +
            require 'walletone/fields'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe Walletone::Fields do
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              describe 'Именованные поля' do
         | 
| 6 | 
            +
                subject { described_class.new }
         | 
| 7 | 
            +
                let(:merchant_id) { 'some_id' }
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                it 'nill by default' do
         | 
| 10 | 
            +
                  expect(subject.WMI_MERCHANT_ID).to eq nil
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
                it { expect(subject.WMI_MERCHANT_ID=merchant_id).to eq merchant_id }
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                context 'установили заранее' do
         | 
| 15 | 
            +
                  let(:merchant_id) { 'some_id2' }
         | 
| 16 | 
            +
                  before { subject.WMI_MERCHANT_ID= merchant_id }
         | 
| 17 | 
            +
                  it { expect(subject.WMI_MERCHANT_ID).to eq merchant_id }
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              describe '#initialize' do
         | 
| 23 | 
            +
                let(:fail_url) { 'someurl' }
         | 
| 24 | 
            +
                let(:attrs)   { { 'WMI_FAIL_URL' => fail_url } }
         | 
| 25 | 
            +
                subject { described_class.new attrs }
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                it { expect(subject.WMI_FAIL_URL).to eq fail_url }
         | 
| 28 | 
            +
                it { expect(subject['WMI_FAIL_URL']).to eq fail_url }
         | 
| 29 | 
            +
                it { expect(subject[:WMI_FAIL_URL]).to eq fail_url }
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              describe '#fetch as keys and keys maybe as symbols or as strings' do
         | 
| 33 | 
            +
                let(:fail_url) { 'someurl' }
         | 
| 34 | 
            +
                let(:attrs)   { { WMI_FAIL_URL: fail_url } }
         | 
| 35 | 
            +
                subject { described_class.new attrs }
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                it { expect(subject[:WMI_FAIL_URL]).to eq fail_url }
         | 
| 38 | 
            +
                it { expect(subject['WMI_FAIL_URL']).to eq fail_url }
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
             | 
| 42 | 
            +
              describe '#as_list' do
         | 
| 43 | 
            +
                let(:fail_url)  { 'someurl' }
         | 
| 44 | 
            +
                let(:pt_enabled) { ['WebMoneyRUB', 'WebMoneyUSD'] }
         | 
| 45 | 
            +
                let(:attrs)   { { WMI_FAIL_URL: fail_url, WMI_PTENABLED: pt_enabled } }
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                subject { described_class.new( attrs ).as_list }
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                it 'получаем список полей в строчном варианте' do
         | 
| 50 | 
            +
                  expect(subject).to eq [["WMI_FAIL_URL", "someurl"], ["WMI_PTENABLED", 'WebMoneyRUB'], ["WMI_PTENABLED", 'WebMoneyUSD']]
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            end
         | 
| @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            require 'walletone/form'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe Walletone::Form do
         | 
| 4 | 
            +
              let(:payment) { Fabricate :payment }
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              subject { described_class.new payment: payment }
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              describe '#checkout_url' do
         | 
| 9 | 
            +
                it { expect(subject.checkout_url).to eq Walletone.config.web_checkout_url }
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              describe '#hidden_fields_tags' do
         | 
| 13 | 
            +
                it { expect(subject.hidden_fields_tags).to include 'input name' }
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              describe '#options' do
         | 
| 17 | 
            +
                it { expect(subject.options).to be_a Hash }
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            end
         | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            require 'walletone/middleware'
         | 
| 2 | 
            +
            require 'walletone/middleware/base'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            describe Walletone::Middleware::Base do
         | 
| 5 | 
            +
              let(:middleware) { described_class.new }
         | 
| 6 | 
            +
              let!(:params) { { cp1251: 'привет'.encode('windows-1251') } }
         | 
| 7 | 
            +
              subject! { Rack::MockRequest.new(middleware).post('/', params: params ) }
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              it do
         | 
| 10 | 
            +
                expect(subject.status).to eq 200
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              it do
         | 
| 14 | 
            +
                expect(subject.body).to eq 'WMI_RESULT=RETRY&WMI_DESCRIPTION=Walletone middleware perform method is not implemented'
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              #Walletone.notify_callback = lambda do |resp, env|
         | 
| 18 | 
            +
              #expect(resp.param(:cp1251).first).to eq('привет')
         | 
| 19 | 
            +
              #resp.ok
         | 
| 20 | 
            +
              #end
         | 
| 21 | 
            +
              #end
         | 
| 22 | 
            +
            end
         | 
| @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            require 'walletone/middleware'
         | 
| 2 | 
            +
            require 'walletone/middleware/callback'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            describe Walletone::Middleware::Callback do
         | 
| 5 | 
            +
              let(:callback) { -> (notify, env) { } }
         | 
| 6 | 
            +
              let(:middleware) { described_class.new callback }
         | 
| 7 | 
            +
              let!(:params) { { cp1251: 'привет'.encode('windows-1251') } }
         | 
| 8 | 
            +
              subject! { Rack::MockRequest.new(middleware).post('/', params: params ) }
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              it do
         | 
| 11 | 
            +
                expect(subject.body).to eq 'WMI_RESULT=OK' 
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            end
         | 
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            require 'walletone/notification'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe Walletone::Notification do
         | 
| 4 | 
            +
              let(:secret)    { '623577687055676e43446f6c474f385165356e64556e474e5d7366' }
         | 
| 5 | 
            +
              let(:signature) { '0v/qXN0eonX2+8AuiLLONQ==' }
         | 
| 6 | 
            +
              let(:raw_params) do
         | 
| 7 | 
            +
                {
         | 
| 8 | 
            +
                  'WMI_MERCHANT_ID'         => '127696943127',
         | 
| 9 | 
            +
                  'WMI_CURRENCY_ID'         => '643',
         | 
| 10 | 
            +
                  'WMI_PAYMENT_AMOUNT'      => '3000.00',
         | 
| 11 | 
            +
                  'WMI_AUTO_ACCEPT'         => '1',
         | 
| 12 | 
            +
                  'WMI_COMMISSION_AMOUNT'   => '120.00',
         | 
| 13 | 
            +
                  'WMI_CREATE_DATE'         => '2014-12-16 20:04:27',
         | 
| 14 | 
            +
                  'WMI_DESCRIPTION'         => 'Браслет цепочка Хамса с цирконами золоченый',
         | 
| 15 | 
            +
                  'WMI_EXPIRED_DATE'        => '2015-01-16 20:04:27',
         | 
| 16 | 
            +
                  'WMI_EXTERNAL_ACCOUNT_ID' => '4276********3093',
         | 
| 17 | 
            +
                  'WMI_LAST_NOTIFY_DATE'    => '2014-12-16 20:07:44',
         | 
| 18 | 
            +
                  'WMI_NOTIFY_COUNT'        => '3',
         | 
| 19 | 
            +
                  'WMI_ORDER_ID'            => '346935973926',
         | 
| 20 | 
            +
                  'WMI_ORDER_STATE'         => 'Accepted',
         | 
| 21 | 
            +
                  'WMI_PAYMENT_NO'          => 'kiosk:5-604-pro',
         | 
| 22 | 
            +
                  'WMI_PAYMENT_TYPE'        => 'CreditCardRUB',
         | 
| 23 | 
            +
                  'WMI_FAIL_URL'            => 'http://wannabe.kiiiosk.ru/payments/w1/failure',
         | 
| 24 | 
            +
                  'WMI_SUCCESS_URL'         => 'http://wannabe.kiiiosk.ru/payments/w1/success',
         | 
| 25 | 
            +
                  'WMI_UPDATE_DATE'         => '2014-12-16 20:05:51',
         | 
| 26 | 
            +
                  'WMI_SIGNATURE'           => signature
         | 
| 27 | 
            +
                }
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
              subject { described_class.new(raw_params) }
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              context '#valid?' do
         | 
| 32 | 
            +
                it 'compare signatures from request and response' do
         | 
| 33 | 
            +
                  expect( subject.valid?(secret) ).to be_truthy
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
            end
         | 
| @@ -0,0 +1,60 @@ | |
| 1 | 
            +
            describe Walletone::Payment do
         | 
| 2 | 
            +
              let(:merchant_id) { 127830694690 }
         | 
| 3 | 
            +
              let(:secret_key) { '3475706857624f46344e573753316e387c396e5f4b54767b796c4c' }
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              subject { described_class.new fields }
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              context '.new' do
         | 
| 8 | 
            +
                let(:fields) { Walletone::Fields.new WMI_MERCHANT_ID: merchant_id }
         | 
| 9 | 
            +
                it 'works' do
         | 
| 10 | 
            +
                  expect(subject.WMI_MERCHANT_ID).to eq(merchant_id)
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              context '#sign' do
         | 
| 15 | 
            +
                let(:signature) { 'uPmNH+YVl154kSH9hQdWNA==' }
         | 
| 16 | 
            +
                let(:fields) { Walletone::Fields.new({
         | 
| 17 | 
            +
                  WMI_MERCHANT_ID:    127830694690,
         | 
| 18 | 
            +
                  WMI_PAYMENT_AMOUNT: 100.00,
         | 
| 19 | 
            +
                  WMI_PAYMENT_NO:     '123-45',
         | 
| 20 | 
            +
                  WMI_CURRENCY_ID:    643,
         | 
| 21 | 
            +
                  WMI_DESCRIPTION:    'payment test',
         | 
| 22 | 
            +
                  WMI_EXPIRED_DATE:   Time.new(2019, 12, 31, 23, 59, 59, "+00:00").utc,
         | 
| 23 | 
            +
                  WMI_SUCCESS_URL:    'http://w1.artemeff.com/w1/success',
         | 
| 24 | 
            +
                  WMI_FAIL_URL:       'http://w1.artemeff.com/w1/fail'
         | 
| 25 | 
            +
                })
         | 
| 26 | 
            +
                }
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                before do
         | 
| 29 | 
            +
                  subject.sign! secret_key
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                it { expect(subject).to be_frozen }
         | 
| 33 | 
            +
                it { expect(subject).to be_signed }
         | 
| 34 | 
            +
                it { expect(subject.WMI_SIGNATURE).to eq signature }
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              it 'WMI_PAYMENT_AMOUNT' do
         | 
| 39 | 
            +
                payment = Fabricate(:payment, {WMI_PAYMENT_AMOUNT: 1.2})
         | 
| 40 | 
            +
                expect(payment.WMI_PAYMENT_AMOUNT).to eq('1.20')
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              it 'WMI_DESCRIPTION as base64' do
         | 
| 44 | 
            +
                payment = Fabricate(:payment, {WMI_DESCRIPTION: 'test'})
         | 
| 45 | 
            +
                expect(payment.WMI_DESCRIPTION).to eq('BASE64:dGVzdA==')
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              it 'WMI_DESCRIPTION reduces to 250 symbols' do
         | 
| 49 | 
            +
                description = FFaker::Lorem.sentence(128)
         | 
| 50 | 
            +
                payment = Fabricate(:payment, {WMI_DESCRIPTION: description})
         | 
| 51 | 
            +
                text = Base64.decode64(payment.WMI_DESCRIPTION)
         | 
| 52 | 
            +
                expect(text.size).to eq(255)
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              it 'WMI_EXPIRED_DATE as ISO8601' do
         | 
| 56 | 
            +
                date = Time.new(2015, 4, 1, 12, 00, 00)
         | 
| 57 | 
            +
                payment = Fabricate(:payment, {WMI_EXPIRED_DATE: date})
         | 
| 58 | 
            +
                expect(payment.WMI_EXPIRED_DATE).to eq date.iso8601
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
            end
         | 
| @@ -0,0 +1,42 @@ | |
| 1 | 
            +
            require 'walletone/signer'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
             | 
| 4 | 
            +
            describe Walletone::Signer do
         | 
| 5 | 
            +
              let(:secret_key) { '3475706857624f46344e573753316e387c396e5f4b54767b796c4c' }
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              subject { described_class.new fields: fields }
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              context '#sign' do
         | 
| 10 | 
            +
                describe 'variant1' do
         | 
| 11 | 
            +
                  let(:fields) { Walletone::Fields.new(
         | 
| 12 | 
            +
                                   WMI_MERCHANT_ID:    '127830694690',
         | 
| 13 | 
            +
                                   WMI_PAYMENT_AMOUNT: '100.00',
         | 
| 14 | 
            +
                                   WMI_PAYMENT_NO:     '123-45',
         | 
| 15 | 
            +
                                   WMI_CURRENCY_ID:    '643',
         | 
| 16 | 
            +
                                   WMI_DESCRIPTION:    'BASE64:cGF5bWVudCB0ZXN0',
         | 
| 17 | 
            +
                                   WMI_EXPIRED_DATE:   '2019-12-31T23:59:59Z',
         | 
| 18 | 
            +
                                   WMI_SUCCESS_URL:    'http://w1.artemeff.com/w1/success',
         | 
| 19 | 
            +
                                   WMI_FAIL_URL:       'http://w1.artemeff.com/w1/fail'
         | 
| 20 | 
            +
                  )}
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  let(:signature) { 'uPmNH+YVl154kSH9hQdWNA==' }
         | 
| 23 | 
            +
                  it { expect( subject.signature( secret_key ) ).to eq signature }
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                describe 'variant 2 (complex)' do
         | 
| 27 | 
            +
                  let(:payment_types) { ['WebMoneyRUB', 'WebMoneyUSD'] }
         | 
| 28 | 
            +
                  let(:fields) { Walletone::Fields.new({
         | 
| 29 | 
            +
                    WMI_MERCHANT_ID:    127830694690,
         | 
| 30 | 
            +
                    WMI_PAYMENT_AMOUNT: '100.00',
         | 
| 31 | 
            +
                    WMI_CURRENCY_ID:    643,
         | 
| 32 | 
            +
                    WMI_PTENABLED:      payment_types,
         | 
| 33 | 
            +
                    foo:                ['bar', 'baz']
         | 
| 34 | 
            +
                  }) }
         | 
| 35 | 
            +
                  let(:signature) { 'BF737gCE9O3JOR5iqQF1kg==' }
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  it { expect( subject.signature( secret_key ) ).to eq signature }
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              #is_valid = response.valid?('0v/qXN0eonX2+8AuiLLONQ==', secret)
         | 
| 42 | 
            +
            end
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            describe Walletone do
         | 
| 2 | 
            +
              context '.logger' do
         | 
| 3 | 
            +
                it 'has logger' do
         | 
| 4 | 
            +
                  expect(described_class.logger).to be_a(Logger)
         | 
| 5 | 
            +
                end
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              context '.notify_error' do
         | 
| 9 | 
            +
                let(:error) { StandardError.new }
         | 
| 10 | 
            +
                it do
         | 
| 11 | 
            +
                  error_notifier = double
         | 
| 12 | 
            +
                  allow(Walletone.config).to receive(:error_notifier).and_return error_notifier
         | 
| 13 | 
            +
                  expect(error_notifier).to receive(:notify).with(error)
         | 
| 14 | 
            +
                  expect(described_class.logger).to receive(:error)
         | 
| 15 | 
            +
                  described_class.notify_error error
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
    
        data/walletone.gemspec
    ADDED
    
    | @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            # coding: utf-8
         | 
| 2 | 
            +
            $:.push File.expand_path("../lib", __FILE__)
         | 
| 3 | 
            +
            require 'walletone/version'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Gem::Specification.new do |s|
         | 
| 6 | 
            +
              s.name = "walletone"
         | 
| 7 | 
            +
              s.version = Walletone::VERSION
         | 
| 8 | 
            +
              s.authors = ["Danil Pismenny", "Yuri Artemev"]
         | 
| 9 | 
            +
              s.email = ["danil@brandymint.ru", "i@artemeff.com"]
         | 
| 10 | 
            +
              s.homepage = "https://github.com/BrandyMint/walletone"
         | 
| 11 | 
            +
              s.summary       = %q{walleton.com Checkout client}
         | 
| 12 | 
            +
              s.description   = %q{Клиент для приема оплаты через walletone.com}
         | 
| 13 | 
            +
              s.licenses = ['MIT']
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              s.files = %w(README.md LICENSE walletone.gemspec)
         | 
| 16 | 
            +
              s.files += Dir.glob("lib/**/*.rb")
         | 
| 17 | 
            +
              s.files += Dir.glob("spec/**/*")
         | 
| 18 | 
            +
              s.require_paths = ['lib']
         | 
| 19 | 
            +
              s.test_files = Dir.glob("spec/**/*")
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              s.add_dependency 'virtus', '~> 1.0.5'
         | 
| 22 | 
            +
              s.add_dependency 'rack',   '~> 1.6'
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              s.add_development_dependency 'rake'
         | 
| 25 | 
            +
              s.add_development_dependency 'rspec', '~> 3.2.0'
         | 
| 26 | 
            +
              s.add_development_dependency 'ffaker', '>= 2.0.0'
         | 
| 27 | 
            +
              s.add_development_dependency 'fabrication', '~> 2.12.2'
         | 
| 28 | 
            +
              s.add_development_dependency "pry"
         | 
| 29 | 
            +
              s.add_development_dependency "pry-nav"
         | 
| 30 | 
            +
              s.add_development_dependency "webmock"
         | 
| 31 | 
            +
              s.add_development_dependency "guard"
         | 
| 32 | 
            +
              s.add_development_dependency "guard-rspec"
         | 
| 33 | 
            +
              s.add_development_dependency 'guard-ctags-bundler'
         | 
| 34 | 
            +
              s.add_development_dependency 'yard'
         | 
| 35 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,266 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: walletone
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.1.0
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Danil Pismenny
         | 
| 8 | 
            +
            - Yuri Artemev
         | 
| 9 | 
            +
            autorequire: 
         | 
| 10 | 
            +
            bindir: bin
         | 
| 11 | 
            +
            cert_chain: []
         | 
| 12 | 
            +
            date: 2015-04-10 00:00:00.000000000 Z
         | 
| 13 | 
            +
            dependencies:
         | 
| 14 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 15 | 
            +
              name: virtus
         | 
| 16 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 17 | 
            +
                requirements:
         | 
| 18 | 
            +
                - - "~>"
         | 
| 19 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 20 | 
            +
                    version: 1.0.5
         | 
| 21 | 
            +
              type: :runtime
         | 
| 22 | 
            +
              prerelease: false
         | 
| 23 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 24 | 
            +
                requirements:
         | 
| 25 | 
            +
                - - "~>"
         | 
| 26 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 27 | 
            +
                    version: 1.0.5
         | 
| 28 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 29 | 
            +
              name: rack
         | 
| 30 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 31 | 
            +
                requirements:
         | 
| 32 | 
            +
                - - "~>"
         | 
| 33 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 34 | 
            +
                    version: '1.6'
         | 
| 35 | 
            +
              type: :runtime
         | 
| 36 | 
            +
              prerelease: false
         | 
| 37 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 38 | 
            +
                requirements:
         | 
| 39 | 
            +
                - - "~>"
         | 
| 40 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 41 | 
            +
                    version: '1.6'
         | 
| 42 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 43 | 
            +
              name: rake
         | 
| 44 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 45 | 
            +
                requirements:
         | 
| 46 | 
            +
                - - ">="
         | 
| 47 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 48 | 
            +
                    version: '0'
         | 
| 49 | 
            +
              type: :development
         | 
| 50 | 
            +
              prerelease: false
         | 
| 51 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 52 | 
            +
                requirements:
         | 
| 53 | 
            +
                - - ">="
         | 
| 54 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 55 | 
            +
                    version: '0'
         | 
| 56 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 57 | 
            +
              name: rspec
         | 
| 58 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 59 | 
            +
                requirements:
         | 
| 60 | 
            +
                - - "~>"
         | 
| 61 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 62 | 
            +
                    version: 3.2.0
         | 
| 63 | 
            +
              type: :development
         | 
| 64 | 
            +
              prerelease: false
         | 
| 65 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 66 | 
            +
                requirements:
         | 
| 67 | 
            +
                - - "~>"
         | 
| 68 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 69 | 
            +
                    version: 3.2.0
         | 
| 70 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 71 | 
            +
              name: ffaker
         | 
| 72 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 73 | 
            +
                requirements:
         | 
| 74 | 
            +
                - - ">="
         | 
| 75 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 76 | 
            +
                    version: 2.0.0
         | 
| 77 | 
            +
              type: :development
         | 
| 78 | 
            +
              prerelease: false
         | 
| 79 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 80 | 
            +
                requirements:
         | 
| 81 | 
            +
                - - ">="
         | 
| 82 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 83 | 
            +
                    version: 2.0.0
         | 
| 84 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 85 | 
            +
              name: fabrication
         | 
| 86 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 87 | 
            +
                requirements:
         | 
| 88 | 
            +
                - - "~>"
         | 
| 89 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 90 | 
            +
                    version: 2.12.2
         | 
| 91 | 
            +
              type: :development
         | 
| 92 | 
            +
              prerelease: false
         | 
| 93 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 94 | 
            +
                requirements:
         | 
| 95 | 
            +
                - - "~>"
         | 
| 96 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 97 | 
            +
                    version: 2.12.2
         | 
| 98 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 99 | 
            +
              name: pry
         | 
| 100 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 101 | 
            +
                requirements:
         | 
| 102 | 
            +
                - - ">="
         | 
| 103 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 104 | 
            +
                    version: '0'
         | 
| 105 | 
            +
              type: :development
         | 
| 106 | 
            +
              prerelease: false
         | 
| 107 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 108 | 
            +
                requirements:
         | 
| 109 | 
            +
                - - ">="
         | 
| 110 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 111 | 
            +
                    version: '0'
         | 
| 112 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 113 | 
            +
              name: pry-nav
         | 
| 114 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 115 | 
            +
                requirements:
         | 
| 116 | 
            +
                - - ">="
         | 
| 117 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 118 | 
            +
                    version: '0'
         | 
| 119 | 
            +
              type: :development
         | 
| 120 | 
            +
              prerelease: false
         | 
| 121 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 122 | 
            +
                requirements:
         | 
| 123 | 
            +
                - - ">="
         | 
| 124 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 125 | 
            +
                    version: '0'
         | 
| 126 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 127 | 
            +
              name: webmock
         | 
| 128 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 129 | 
            +
                requirements:
         | 
| 130 | 
            +
                - - ">="
         | 
| 131 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 132 | 
            +
                    version: '0'
         | 
| 133 | 
            +
              type: :development
         | 
| 134 | 
            +
              prerelease: false
         | 
| 135 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 136 | 
            +
                requirements:
         | 
| 137 | 
            +
                - - ">="
         | 
| 138 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 139 | 
            +
                    version: '0'
         | 
| 140 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 141 | 
            +
              name: guard
         | 
| 142 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 143 | 
            +
                requirements:
         | 
| 144 | 
            +
                - - ">="
         | 
| 145 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 146 | 
            +
                    version: '0'
         | 
| 147 | 
            +
              type: :development
         | 
| 148 | 
            +
              prerelease: false
         | 
| 149 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 150 | 
            +
                requirements:
         | 
| 151 | 
            +
                - - ">="
         | 
| 152 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 153 | 
            +
                    version: '0'
         | 
| 154 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 155 | 
            +
              name: guard-rspec
         | 
| 156 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 157 | 
            +
                requirements:
         | 
| 158 | 
            +
                - - ">="
         | 
| 159 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 160 | 
            +
                    version: '0'
         | 
| 161 | 
            +
              type: :development
         | 
| 162 | 
            +
              prerelease: false
         | 
| 163 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 164 | 
            +
                requirements:
         | 
| 165 | 
            +
                - - ">="
         | 
| 166 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 167 | 
            +
                    version: '0'
         | 
| 168 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 169 | 
            +
              name: guard-ctags-bundler
         | 
| 170 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 171 | 
            +
                requirements:
         | 
| 172 | 
            +
                - - ">="
         | 
| 173 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 174 | 
            +
                    version: '0'
         | 
| 175 | 
            +
              type: :development
         | 
| 176 | 
            +
              prerelease: false
         | 
| 177 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 178 | 
            +
                requirements:
         | 
| 179 | 
            +
                - - ">="
         | 
| 180 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 181 | 
            +
                    version: '0'
         | 
| 182 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 183 | 
            +
              name: yard
         | 
| 184 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 185 | 
            +
                requirements:
         | 
| 186 | 
            +
                - - ">="
         | 
| 187 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 188 | 
            +
                    version: '0'
         | 
| 189 | 
            +
              type: :development
         | 
| 190 | 
            +
              prerelease: false
         | 
| 191 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 192 | 
            +
                requirements:
         | 
| 193 | 
            +
                - - ">="
         | 
| 194 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 195 | 
            +
                    version: '0'
         | 
| 196 | 
            +
            description: "Клиент для приема оплаты через walletone.com"
         | 
| 197 | 
            +
            email:
         | 
| 198 | 
            +
            - danil@brandymint.ru
         | 
| 199 | 
            +
            - i@artemeff.com
         | 
| 200 | 
            +
            executables: []
         | 
| 201 | 
            +
            extensions: []
         | 
| 202 | 
            +
            extra_rdoc_files: []
         | 
| 203 | 
            +
            files:
         | 
| 204 | 
            +
            - LICENSE
         | 
| 205 | 
            +
            - README.md
         | 
| 206 | 
            +
            - lib/walletone.rb
         | 
| 207 | 
            +
            - lib/walletone/errors/bad_url_error.rb
         | 
| 208 | 
            +
            - lib/walletone/errors/walletone_error.rb
         | 
| 209 | 
            +
            - lib/walletone/fields.rb
         | 
| 210 | 
            +
            - lib/walletone/form.rb
         | 
| 211 | 
            +
            - lib/walletone/middleware.rb
         | 
| 212 | 
            +
            - lib/walletone/middleware/base.rb
         | 
| 213 | 
            +
            - lib/walletone/middleware/callback.rb
         | 
| 214 | 
            +
            - lib/walletone/notification.rb
         | 
| 215 | 
            +
            - lib/walletone/payment.rb
         | 
| 216 | 
            +
            - lib/walletone/signer.rb
         | 
| 217 | 
            +
            - lib/walletone/version.rb
         | 
| 218 | 
            +
            - spec/fabricators/fields_fabricator.rb
         | 
| 219 | 
            +
            - spec/fabricators/payment_fabricator.rb
         | 
| 220 | 
            +
            - spec/spec_helper.rb
         | 
| 221 | 
            +
            - spec/walletone/fields_spec.rb
         | 
| 222 | 
            +
            - spec/walletone/form_spec.rb
         | 
| 223 | 
            +
            - spec/walletone/middleware/base_spec.rb
         | 
| 224 | 
            +
            - spec/walletone/middleware/callback_spec.rb
         | 
| 225 | 
            +
            - spec/walletone/notification_spec.rb
         | 
| 226 | 
            +
            - spec/walletone/payment_spec.rb
         | 
| 227 | 
            +
            - spec/walletone/signer_spec.rb
         | 
| 228 | 
            +
            - spec/walletone_spec.rb
         | 
| 229 | 
            +
            - walletone.gemspec
         | 
| 230 | 
            +
            homepage: https://github.com/BrandyMint/walletone
         | 
| 231 | 
            +
            licenses:
         | 
| 232 | 
            +
            - MIT
         | 
| 233 | 
            +
            metadata: {}
         | 
| 234 | 
            +
            post_install_message: 
         | 
| 235 | 
            +
            rdoc_options: []
         | 
| 236 | 
            +
            require_paths:
         | 
| 237 | 
            +
            - lib
         | 
| 238 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 239 | 
            +
              requirements:
         | 
| 240 | 
            +
              - - ">="
         | 
| 241 | 
            +
                - !ruby/object:Gem::Version
         | 
| 242 | 
            +
                  version: '0'
         | 
| 243 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 244 | 
            +
              requirements:
         | 
| 245 | 
            +
              - - ">="
         | 
| 246 | 
            +
                - !ruby/object:Gem::Version
         | 
| 247 | 
            +
                  version: '0'
         | 
| 248 | 
            +
            requirements: []
         | 
| 249 | 
            +
            rubyforge_project: 
         | 
| 250 | 
            +
            rubygems_version: 2.2.2
         | 
| 251 | 
            +
            signing_key: 
         | 
| 252 | 
            +
            specification_version: 4
         | 
| 253 | 
            +
            summary: walleton.com Checkout client
         | 
| 254 | 
            +
            test_files:
         | 
| 255 | 
            +
            - spec/walletone/payment_spec.rb
         | 
| 256 | 
            +
            - spec/walletone/fields_spec.rb
         | 
| 257 | 
            +
            - spec/walletone/form_spec.rb
         | 
| 258 | 
            +
            - spec/walletone/notification_spec.rb
         | 
| 259 | 
            +
            - spec/walletone/middleware/callback_spec.rb
         | 
| 260 | 
            +
            - spec/walletone/middleware/base_spec.rb
         | 
| 261 | 
            +
            - spec/walletone/signer_spec.rb
         | 
| 262 | 
            +
            - spec/spec_helper.rb
         | 
| 263 | 
            +
            - spec/fabricators/payment_fabricator.rb
         | 
| 264 | 
            +
            - spec/fabricators/fields_fabricator.rb
         | 
| 265 | 
            +
            - spec/walletone_spec.rb
         | 
| 266 | 
            +
            has_rdoc: 
         |