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.
- checksums.yaml +7 -0
- data/.gitignore +25 -0
- data/.rspec +1 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/app/controllers/perfect_money_merchant/payments_controller.rb +39 -0
- data/app/models/perfect_money_merchant/account.rb +152 -0
- data/app/models/perfect_money_merchant/payment.rb +12 -0
- data/config/routes.rb +9 -0
- data/lib/generators/perfect_money_merchant/perfect_money_merchant_generator.rb +19 -0
- data/lib/generators/perfect_money_merchant/templates/migration.rb +42 -0
- data/lib/perfect_money_merchant/api.rb +52 -0
- data/lib/perfect_money_merchant/configuration.rb +46 -0
- data/lib/perfect_money_merchant/engine.rb +6 -0
- data/lib/perfect_money_merchant/error.rb +10 -0
- data/lib/perfect_money_merchant/railtie.rb +8 -0
- data/lib/perfect_money_merchant/sci.rb +121 -0
- data/lib/perfect_money_merchant/sci_response.rb +116 -0
- data/lib/perfect_money_merchant/version.rb +3 -0
- data/lib/perfect_money_merchant.rb +23 -0
- data/perfect_money_merchant.gemspec +33 -0
- data/spec/controllers/perfect_money_merchant/payments_controller_spec.rb +57 -0
- data/spec/models/perfect_money_merchant/account_spec.rb +7 -0
- data/spec/perfect_money_merchant/configuration_spec.rb +51 -0
- data/spec/perfect_money_merchant/sci_response_spec.rb +50 -0
- data/spec/perfect_money_merchant/sci_spec.rb +49 -0
- data/spec/rails_test_app/Gemfile +8 -0
- data/spec/rails_test_app/Rakefile +6 -0
- data/spec/rails_test_app/app/assets/javascripts/application.js +16 -0
- data/spec/rails_test_app/app/assets/stylesheets/application.css +15 -0
- data/spec/rails_test_app/app/controllers/application_controller.rb +5 -0
- data/spec/rails_test_app/app/helpers/application_helper.rb +2 -0
- data/spec/rails_test_app/app/views/layouts/application.html.erb +14 -0
- data/spec/rails_test_app/bin/bundle +3 -0
- data/spec/rails_test_app/bin/rails +4 -0
- data/spec/rails_test_app/bin/rake +4 -0
- data/spec/rails_test_app/config/application.rb +23 -0
- data/spec/rails_test_app/config/boot.rb +4 -0
- data/spec/rails_test_app/config/database.yml +25 -0
- data/spec/rails_test_app/config/environment.rb +8 -0
- data/spec/rails_test_app/config/environments/development.rb +42 -0
- data/spec/rails_test_app/config/environments/production.rb +78 -0
- data/spec/rails_test_app/config/environments/test.rb +39 -0
- data/spec/rails_test_app/config/initializers/assets.rb +8 -0
- data/spec/rails_test_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_test_app/config/initializers/cookies_serializer.rb +3 -0
- data/spec/rails_test_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/rails_test_app/config/initializers/inflections.rb +16 -0
- data/spec/rails_test_app/config/initializers/mime_types.rb +4 -0
- data/spec/rails_test_app/config/initializers/session_store.rb +3 -0
- data/spec/rails_test_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails_test_app/config/locales/en.yml +23 -0
- data/spec/rails_test_app/config/routes.rb +64 -0
- data/spec/rails_test_app/config/secrets.yml +22 -0
- data/spec/rails_test_app/config.ru +5 -0
- data/spec/rails_test_app/db/seeds.rb +0 -0
- data/spec/rails_test_app/public/404.html +67 -0
- data/spec/rails_test_app/public/422.html +67 -0
- data/spec/rails_test_app/public/500.html +66 -0
- data/spec/rails_test_app/public/favicon.ico +0 -0
- data/spec/rails_test_app/public/robots.txt +5 -0
- data/spec/spec_helper.rb +66 -0
- 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
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,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,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
|