perfect_money_merchant 0.1.0.alpha.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +25 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +5 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +29 -0
  7. data/Rakefile +2 -0
  8. data/app/controllers/perfect_money_merchant/payments_controller.rb +39 -0
  9. data/app/models/perfect_money_merchant/account.rb +152 -0
  10. data/app/models/perfect_money_merchant/payment.rb +12 -0
  11. data/config/routes.rb +9 -0
  12. data/lib/generators/perfect_money_merchant/perfect_money_merchant_generator.rb +19 -0
  13. data/lib/generators/perfect_money_merchant/templates/migration.rb +42 -0
  14. data/lib/perfect_money_merchant/api.rb +52 -0
  15. data/lib/perfect_money_merchant/configuration.rb +46 -0
  16. data/lib/perfect_money_merchant/engine.rb +6 -0
  17. data/lib/perfect_money_merchant/error.rb +10 -0
  18. data/lib/perfect_money_merchant/railtie.rb +8 -0
  19. data/lib/perfect_money_merchant/sci.rb +121 -0
  20. data/lib/perfect_money_merchant/sci_response.rb +116 -0
  21. data/lib/perfect_money_merchant/version.rb +3 -0
  22. data/lib/perfect_money_merchant.rb +23 -0
  23. data/perfect_money_merchant.gemspec +33 -0
  24. data/spec/controllers/perfect_money_merchant/payments_controller_spec.rb +57 -0
  25. data/spec/models/perfect_money_merchant/account_spec.rb +7 -0
  26. data/spec/perfect_money_merchant/configuration_spec.rb +51 -0
  27. data/spec/perfect_money_merchant/sci_response_spec.rb +50 -0
  28. data/spec/perfect_money_merchant/sci_spec.rb +49 -0
  29. data/spec/rails_test_app/Gemfile +8 -0
  30. data/spec/rails_test_app/Rakefile +6 -0
  31. data/spec/rails_test_app/app/assets/javascripts/application.js +16 -0
  32. data/spec/rails_test_app/app/assets/stylesheets/application.css +15 -0
  33. data/spec/rails_test_app/app/controllers/application_controller.rb +5 -0
  34. data/spec/rails_test_app/app/helpers/application_helper.rb +2 -0
  35. data/spec/rails_test_app/app/views/layouts/application.html.erb +14 -0
  36. data/spec/rails_test_app/bin/bundle +3 -0
  37. data/spec/rails_test_app/bin/rails +4 -0
  38. data/spec/rails_test_app/bin/rake +4 -0
  39. data/spec/rails_test_app/config/application.rb +23 -0
  40. data/spec/rails_test_app/config/boot.rb +4 -0
  41. data/spec/rails_test_app/config/database.yml +25 -0
  42. data/spec/rails_test_app/config/environment.rb +8 -0
  43. data/spec/rails_test_app/config/environments/development.rb +42 -0
  44. data/spec/rails_test_app/config/environments/production.rb +78 -0
  45. data/spec/rails_test_app/config/environments/test.rb +39 -0
  46. data/spec/rails_test_app/config/initializers/assets.rb +8 -0
  47. data/spec/rails_test_app/config/initializers/backtrace_silencers.rb +7 -0
  48. data/spec/rails_test_app/config/initializers/cookies_serializer.rb +3 -0
  49. data/spec/rails_test_app/config/initializers/filter_parameter_logging.rb +4 -0
  50. data/spec/rails_test_app/config/initializers/inflections.rb +16 -0
  51. data/spec/rails_test_app/config/initializers/mime_types.rb +4 -0
  52. data/spec/rails_test_app/config/initializers/session_store.rb +3 -0
  53. data/spec/rails_test_app/config/initializers/wrap_parameters.rb +14 -0
  54. data/spec/rails_test_app/config/locales/en.yml +23 -0
  55. data/spec/rails_test_app/config/routes.rb +64 -0
  56. data/spec/rails_test_app/config/secrets.yml +22 -0
  57. data/spec/rails_test_app/config.ru +5 -0
  58. data/spec/rails_test_app/db/seeds.rb +0 -0
  59. data/spec/rails_test_app/public/404.html +67 -0
  60. data/spec/rails_test_app/public/422.html +67 -0
  61. data/spec/rails_test_app/public/500.html +66 -0
  62. data/spec/rails_test_app/public/favicon.ico +0 -0
  63. data/spec/rails_test_app/public/robots.txt +5 -0
  64. data/spec/spec_helper.rb +66 -0
  65. metadata +302 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a15618b2624daa6dd0000ba9dcff25daf44a8c51
4
+ data.tar.gz: 41e23e1da172562c2a07c255597e3ce0b4e05080
5
+ SHA512:
6
+ metadata.gz: aef70b664353c315ac9b5a18d0bbe46b681d2cceaac9d9db3ee034e23a8800cfd48c809bfbf0848e7be635a0b74713d91fc39aa48517c8323d307bc4cbfdb1a7
7
+ data.tar.gz: db642c4b91ed3bc3bcfeb2fdef8c566150269a1fddb8ac05bb2ab90bcbcba1d1c404798617b6de99f9db7f93de7d56df163cfcf5597cccdb604647f762eb65fa
data/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ *.log
23
+ .keep
24
+ .rvmrc
25
+ **/migrate/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --format documentation --color
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+
4
+ # Specify your gem's dependencies in perfect_money_merchant.gemspec
5
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 BroderickBrockman
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # PerfectMoneyMerchant
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'perfect_money_merchant'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install perfect_money_merchant
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( https://github.com/[my-github-username]/perfect_money_merchant/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,39 @@
1
+ module PerfectMoneyMerchant
2
+ class PaymentsController < ActionController::Base
3
+ def status
4
+ if Payment.exists?(payment_batch_num: sci_params.payment_batch_num)
5
+ raise StandardError.new('payment exists')
6
+ end
7
+
8
+ SCIResponse.new(sci_params).tap { |obj| obj.set_secret_key(Account::Query.new.get_secret_key(sci_params.payee_account)) }.verify!
9
+
10
+ Payment.create!(
11
+ payment_batch_num: sci_params.payment_batch_num,
12
+ payment_id: sci_params.payment_id,
13
+ payment_amount: sci_params.payment_amount,
14
+ payer_account: sci_params.payer_account,
15
+ payee_account: sci_params.payee_account,
16
+ )
17
+
18
+ Configuration.config.tasks[sci_params.payment_purpose].tap { |task| task.call(sci_params) unless task.nil? }
19
+
20
+ render status: 200, nothing: true
21
+ rescue StandardError => exception
22
+ render status: 400, inline: 'error occurred'
23
+ end
24
+
25
+ def success
26
+ render status: 200, nothing: true
27
+ end
28
+
29
+ def error
30
+ render status: 400, nothing: true
31
+ end
32
+
33
+ private
34
+
35
+ def sci_params
36
+ @sci_params ||= Hashie::Mash.new(params.map { |k, v| [k.downcase, v] }.inject({}) { |hash, param| hash.merge!(param[0] => param[1]) })
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,152 @@
1
+ module PerfectMoneyMerchant
2
+ class Account < ActiveRecord::Base
3
+ class Unit < ActiveRecord::Base
4
+ self.table_name = 'perfect_money_merchant_account_units'
5
+
6
+ belongs_to :account, class_name: 'Account', foreign_key: :account_id
7
+
8
+ validates :currency, :code_number, presence: true
9
+ validates :currency, inclusion: { in: %w(usd eur) }
10
+ validates :code_number, format: { with: /\A[EU]\d{7}\z/ }
11
+
12
+ # transfer money to other PerfectMoney account
13
+ # @param [String] PerfectMoney account code number
14
+ # @param [Float] just desirable amount to trasnfer
15
+ # @return [Hash] PerfectMoney http response body
16
+ def transfer!(payee_account, amount)
17
+ Api.new.transfer(
18
+ AccountID: self.account.login,
19
+ PassPhrase: self.account.password,
20
+ Payer_Account: self.code_number,
21
+ Payee_Account: payee_account,
22
+ Amount: amount
23
+ )
24
+ end
25
+
26
+ after_create :sync_with_pm_server
27
+
28
+ # unit synchronization with perfect money server
29
+ def sync_with_pm_server
30
+ account.sync_with_pm_server
31
+ end
32
+ end
33
+
34
+ class Query
35
+ attr_reader :relation
36
+
37
+ def initialize(relation = Account.all)
38
+ @relation = relation
39
+ end
40
+
41
+ # find account by unit's code number
42
+ # @param [String] code number
43
+ # @return [PerfectMoneyMerchant::Account]
44
+ def find_by_unit_code_number(code_number)
45
+ relation.joins(:units).where(perfect_money_merchant_account_units: { code_number: code_number }).take(1).first
46
+ end
47
+
48
+ def get_secret_key(code_number)
49
+ find_by_unit_code_number(code_number).try(:secret_key)
50
+ end
51
+ end
52
+
53
+ self.table_name = 'perfect_money_merchant_accounts'
54
+
55
+ has_many :units, dependent: :destroy
56
+ accepts_nested_attributes_for :units, :reject_if => :all_blank, :allow_destroy => true
57
+
58
+ validates :login, :password, :secret_key, presence: true
59
+
60
+ # Synchronize account unit balances with Perfect Money server via PerfectMoneyMerchant::Api
61
+ # @see PerfectMoneyMerchant::Api PerfectMoneyMerchant Api
62
+ # @return [PerfectMoneyMerchant::Account] itself
63
+ def sync_with_pm_server
64
+ response = Api.new.balance(AccountID: login, PassPhrase: password)
65
+ response.each_pair do |code_number, balance|
66
+ units.each do |unit|
67
+ unit.update!(balance: balance) if unit.code_number == code_number
68
+ end
69
+ end
70
+ self
71
+ end
72
+
73
+ # Transfer money to whatever perfect money account
74
+ # @param [String] payee_account payee account code number
75
+ # @param [Float] amount amount of money
76
+ # @return [Hash] Perfect Money http response body
77
+ def transfer!(payee_account, amount)
78
+ if payee_account =~ /\A([UE])\d{7}\z/
79
+ currency = $1 == 'U' ? 'usd' : 'eur'
80
+ unit = units.where(currency: currency).where('balance > ?', amount).order('balance DESC').take(1).first
81
+ if unit.nil?
82
+ exception = BasicError.new('no unit was found')
83
+ exception.add_error(:unit, :not_found)
84
+ raise exception
85
+ else
86
+ unit.transfer!(payee_account, amount).tap do |params|
87
+ sync_with_pm_server
88
+ Payment.create(
89
+ payment_batch_num: params.payment_batch_num,
90
+ payment_id: params.payment_id,
91
+ payment_amount: params.payment_amount,
92
+ payer_account: params.payer_account,
93
+ payee_account: params.payee_account
94
+ )
95
+ end
96
+ end
97
+ else
98
+ exception = BasicError.new('invalid payee_account')
99
+ exception.add_error(:payee_account, :invalid)
100
+ raise exception
101
+ end
102
+ end
103
+
104
+ class << self
105
+ # Obtain the most suitable deposit account from database
106
+ # @param [String] currency currency like 'usd' or 'eur'
107
+ # @return [String] PerfectMoney account code number
108
+ def obtain_deposit_account(currency)
109
+ currency = currency.to_s.downcase
110
+ unit = Account::Unit.
111
+ joins(:account).
112
+ where.not(perfect_money_merchant_accounts: { secret_key: nil }).
113
+ where(currency: currency).
114
+ order('balance ASC').take(1).first
115
+ if unit
116
+ unit.code_number
117
+ else
118
+ raise BasicError.new('no unit was found').tap do |exception|
119
+ exception.add_error(:unit, :not_found)
120
+ end
121
+ end
122
+ end
123
+
124
+ # Transfer money to whatever perfect money account
125
+ # @param [String] payee_account payee account code number
126
+ # @param [Float] amount amount of money
127
+ # @return [Hash] Perfect Money http response body
128
+ #
129
+ # PerfectMoneyMerchant::Account.transfer('U8259997',0.01)
130
+ def transfer!(payee_account, amount)
131
+ if payee_account =~ /\A([UE])\d{7}\z/
132
+ currency = $1 == 'U' ? 'usd' : 'eur'
133
+ account = joins(:units).
134
+ where.not(login: nil, password: nil).
135
+ where(perfect_money_merchant_account_units: { currency: currency }).
136
+ where('perfect_money_merchant_account_units.balance > ?', amount).
137
+ order('perfect_money_merchant_account_units.balance DESC').
138
+ take(1).first
139
+ if account.nil?
140
+ raise StandardError.new('account nil')
141
+ else
142
+ account.transfer!(payee_account, amount)
143
+ end
144
+ else
145
+ raise BasicError.new('invalid payee_account').tap do |exception|
146
+ exception.add_error(:payee_account, :invalid)
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,12 @@
1
+ # =AR Fields
2
+ # +payment_batch_num+
3
+ # +payment_id+
4
+ # +payment_amount+
5
+ # +payer_account+
6
+ # +payee_account+
7
+ #
8
+ class PerfectMoneyMerchant::Payment < ActiveRecord::Base
9
+ self.table_name = 'perfect_money_merchant_payments'
10
+
11
+ validates :payment_batch_num, :payment_amount, :payer_account, :payee_account, presence: true
12
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,9 @@
1
+ Rails.application.routes.draw do
2
+ namespace :perfect_money_merchant do
3
+ resource :payment, only: [] do
4
+ post 'status' #, as: 'perfect_money_merchant_payment_status'
5
+ post 'success' #, as: 'perfect_money_merchant_payment_success'
6
+ post 'error' #, as: 'perfect_money_merchant_payment_error'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ class PerfectMoneyMerchantGenerator < Rails::Generators::Base
2
+ include Rails::Generators::Migration
3
+
4
+ def self.source_root
5
+ @source_root ||= File.join(File.dirname(__FILE__), 'templates')
6
+ end
7
+
8
+ def self.next_migration_number(dirname)
9
+ if ActiveRecord::Base.timestamped_migrations
10
+ Time.new.utc.strftime("%Y%m%d%H%M%S")
11
+ else
12
+ "%.3d" % (current_migration_number(dirname) + 1)
13
+ end
14
+ end
15
+
16
+ def create_migration_file
17
+ migration_template 'migration.rb', 'db/migrate/create_perfect_money_merchant_accounts_and_account_units_and_payments.rb'
18
+ end
19
+ end
@@ -0,0 +1,42 @@
1
+ class CreatePerfectMoneyMerchantAccountsAndAccountUnitsAndPayments < ActiveRecord::Migration
2
+ def up
3
+ create_table :perfect_money_merchant_accounts do |t|
4
+ t.string :login
5
+ t.string :password
6
+ t.string :secret_key
7
+ end
8
+
9
+ create_table :perfect_money_merchant_account_units do |t|
10
+ t.integer :account_id
11
+ t.string :currency
12
+ t.string :code_number
13
+ t.float :balance
14
+ end
15
+
16
+ create_table :perfect_money_merchant_payments do |t|
17
+ t.string :payment_batch_num
18
+ t.string :payment_id
19
+ t.string :payment_amount
20
+ t.string :payer_account
21
+ t.string :payee_account
22
+ t.timestamps
23
+ end
24
+
25
+
26
+ add_index :perfect_money_merchant_payments, :payment_batch_num, :unique => true, name: :pmmp_pbn_unq
27
+ add_index :perfect_money_merchant_accounts, :login, :unique => true, name: :pmma_l_unq
28
+ add_index :perfect_money_merchant_accounts, :secret_key, :unique => true, name: :pmma_sk_unq
29
+ add_index :perfect_money_merchant_account_units, :code_number, :unique => true, name: :pmmam_cn_unq
30
+ end
31
+
32
+ def down
33
+ drop_table :perfect_money_merchant_accounts
34
+ drop_table :perfect_money_merchant_account_units
35
+ drop_table :perfect_money_merchant_payments
36
+
37
+ remove_index :perfect_money_merchant_payments, name: :pmmp_pbn_unq
38
+ remove_index :perfect_money_merchant_accounts, name: :pmma_l_unq
39
+ remove_index :perfect_money_merchant_accounts, name: :pmma_sk_unq
40
+ remove_index :perfect_money_merchant_account_units, name: :pmmam_cn_unq
41
+ end
42
+ end
@@ -0,0 +1,52 @@
1
+ class PerfectMoneyMerchant::Api
2
+ class ParseResponse < ::FaradayMiddleware::ResponseMiddleware
3
+ dependency do
4
+ require 'nokogiri' unless defined? ::Nokogiri
5
+ end
6
+ define_parser do |body|
7
+ Nokogiri::HTML.parse(body).search('//input').inject({}) { |hash, field| hash.merge({ field['name'] => field['value'] }) }
8
+ end
9
+ end
10
+
11
+ Faraday::Response.register_middleware parse_pm_response: ParseResponse
12
+
13
+ API_URL = 'https://perfectmoney.is'.freeze
14
+ METHODS = {
15
+ balance: {
16
+ path: 'acct/balance.asp',
17
+ donwcase_params: false
18
+ },
19
+ transfer: {
20
+ path: 'acct/confirm.asp',
21
+ donwcase_params: true
22
+ }
23
+ }
24
+
25
+ def method_missing(*args, &block)
26
+ if METHODS.include?(args[0])
27
+ response_hash = api_call(METHODS[args[0]][:path], args[1])
28
+ if METHODS[args[0]][:donwcase_params]
29
+ response_hash.map { |k, v| [k.downcase, v] }.inject(Hashie::Mash.new) { |hash, param| hash.merge!(param[0] => param[1]) }
30
+ else
31
+ Hashie::Mash.new(response_hash)
32
+ end
33
+ else
34
+ super
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def api_call(path, params)
41
+ @response = connection.post(path, params)
42
+ @response.body
43
+ end
44
+
45
+ def connection
46
+ @connection ||= ::Faraday.new(url: API_URL.dup) do |faraday|
47
+ faraday.request :url_encoded
48
+ faraday.response :parse_pm_response
49
+ faraday.adapter Faraday.default_adapter
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,46 @@
1
+ module PerfectMoneyMerchant
2
+ class Configuration
3
+ FIELDS = [:payee_name, :payment_units, :status_url, :payment_url, :payment_url_method, :nopayment_url, :nopayment_url_method, :suggested_memo, :verification_secret]
4
+
5
+ class << self
6
+ def configure
7
+ @config ||= Configuration.new
8
+ yield(@config) if block_given?
9
+ # valid_fields!
10
+ @config
11
+ end
12
+
13
+ def config
14
+ @config
15
+ end
16
+
17
+ def valid_fields!
18
+ raise StandardError.new('verification_secret is nil') if @config.verification_secret.nil?
19
+
20
+ # if FIELDS.map { |field_name| @config.instance_variable_get("@#{field_name}") }.include?(nil)
21
+ # raise StandardError.new('all fields must be initiated')
22
+ # end
23
+ end
24
+ end
25
+
26
+ attr_accessor *FIELDS
27
+ attr_reader :tasks
28
+
29
+ def initialize
30
+ @tasks = Hashie::Mash.new
31
+ end
32
+
33
+ def verification_secret
34
+ if @verification_secret
35
+ @verification_secret
36
+ else
37
+ raise StandardError.new('verification_secret is nil')
38
+ end
39
+ end
40
+
41
+ def add_task(task_name, block)
42
+ @tasks ||= Hashie::Mash.new
43
+ @tasks[task_name] = block
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,6 @@
1
+ require 'rails/engine'
2
+ require 'perfect_money_merchant'
3
+
4
+ class PerfectMoneyMerchant::Engine < Rails::Engine #:nodoc:
5
+ config.perfect_money_merchant = PerfectMoneyMerchant
6
+ end
@@ -0,0 +1,10 @@
1
+ module PerfectMoneyMerchant
2
+ class BasicError < StandardError
3
+ attr_reader :errors
4
+
5
+ def add_error(name, value)
6
+ @errors ||= Hashie::Mash.new
7
+ @errors[name] = value # TODO: add I18n
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ require 'rails/railtie'
2
+ require 'perfect_money_merchant'
3
+
4
+ class PerfectMoneyMerchant::Railtie < ::Rails::Railtie #:nodoc:
5
+ # initializer 'perfect_money_merchant.set_routes' do
6
+ # puts Rails.application.routes.url_helpers.class
7
+ # end
8
+ end
@@ -0,0 +1,121 @@
1
+ module PerfectMoneyMerchant
2
+ class SCI
3
+ class << self
4
+ def generate_verification_code(values)
5
+ sha2 = Digest::SHA2.new
6
+ values.each { |value| sha2.update(value.to_s) }
7
+ sha2.update(Configuration.config.verification_secret)
8
+ sha2.to_s
9
+ end
10
+ end
11
+
12
+ attr_reader :payee_name
13
+ attr_reader :payee_account
14
+ attr_reader :payment_id
15
+ attr_reader :payment_amount
16
+ attr_reader :payment_units
17
+ attr_reader :status_url
18
+ attr_reader :payment_url
19
+ attr_reader :payment_url_method
20
+ attr_reader :nopayment_url
21
+ attr_reader :nopayment_url_method
22
+ attr_reader :suggested_memo
23
+ attr_reader :baggage_fields
24
+ attr_reader :payment_purpose
25
+
26
+ def initialize(attributes = {})
27
+ set_defaults
28
+
29
+ set_price(attributes[:price]) if attributes[:price]
30
+ set_currency(attributes[:currency]) if attributes[:currency]
31
+ set_commentary(attributes[:commentary]) if attributes[:commentary]
32
+ set_payee(attributes[:payee]) if attributes[:payee]
33
+ set_title(attributes[:title]) if attributes[:title]
34
+ set_additional(attributes[:additional]) if attributes[:additional]
35
+ set_verification(attributes[:verification]) if attributes[:verification]
36
+ set_purpose(attributes[:purpose]) if attributes[:purpose]
37
+
38
+ verify_fields!
39
+ end
40
+
41
+ def set_price(price)
42
+ @payment_amount = price
43
+ end
44
+
45
+ def set_commentary(commentary)
46
+ @suggested_memo = commentary
47
+ end
48
+
49
+ def set_payee(payee)
50
+ @payee_account = payee
51
+ end
52
+
53
+ def set_title(title)
54
+ @payee_name = title
55
+ end
56
+
57
+ def set_currency(currency)
58
+ @payment_units = currency
59
+ end
60
+
61
+ def set_additional(additional)
62
+ additional.each_pair { |attr_name, attr_value| set_field(attr_name, attr_value) }
63
+ end
64
+
65
+ def set_purpose(purpose)
66
+ @payment_purpose = purpose
67
+ end
68
+
69
+ def set_field(name, value)
70
+ singleton_class.class_eval do
71
+ attr_accessor name
72
+ end
73
+ instance_variable_set("@#{name}", value)
74
+ set_baggage_field(name)
75
+ end
76
+
77
+ def set_verification(fields)
78
+ set_field(:verification_code, self.class.generate_verification_code(fields.map { |field| send(field).to_s }))
79
+ set_field(:verification_fields, fields.join(' '))
80
+ end
81
+
82
+ def to_hash
83
+ instance_variables.inject({}) { |hash, variable| hash.merge({ variable.to_s.delete('@').to_sym => instance_variable_get(variable) }) }
84
+ end
85
+
86
+ private
87
+
88
+ def verify_fields!
89
+ raise StandardError.new('payee_name is nil') if payee_name.nil?
90
+ raise StandardError.new('suggested_memo is nil') if suggested_memo.nil?
91
+ raise StandardError.new('payment_units is nil') if payment_units.nil?
92
+ raise StandardError.new('payee_account is nil') if payee_account.nil?
93
+ raise StandardError.new('status_url is nil') if status_url.nil?
94
+ raise StandardError.new('payment_url is nil') if payment_url.nil?
95
+ raise StandardError.new('payment_url_method is nil') if payment_url_method.nil?
96
+ raise StandardError.new('nopayment_url is nil') if nopayment_url.nil?
97
+ raise StandardError.new('nopayment_url_method is nil') if nopayment_url_method.nil?
98
+ raise StandardError.new('payment_purpose is nil') if payment_purpose.nil?
99
+ end
100
+
101
+ def set_defaults
102
+ config = Configuration.config
103
+
104
+ @payee_name = config.payee_name || 'Perfect Money Merchant'
105
+ @suggested_memo = config.suggested_memo || 'Perfect Money Merchant Payment'
106
+ @payment_units = config.payment_units || 'USD'
107
+ @status_url = config.status_url || '/perfect_money_merchant/payment/status'
108
+ @payment_url = config.payment_url || '/perfect_money_merchant/payment/success'
109
+ @payment_url_method = config.payment_url_method || 'POST'
110
+ @nopayment_url = config.nopayment_url || '/perfect_money_merchant/payment/error'
111
+ @nopayment_url_method = config.nopayment_url_method || 'POST'
112
+ @baggage_fields = ''
113
+ end
114
+
115
+ def set_baggage_field(field_name)
116
+ baggage_fields_tmp = baggage_fields.split(' ')
117
+ baggage_fields_tmp << field_name
118
+ @baggage_fields = baggage_fields_tmp.join(' ')
119
+ end
120
+ end
121
+ end