didww_ups 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/.travis.yml +8 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +34 -0
- data/Rakefile +7 -0
- data/didww_ups.gemspec +28 -0
- data/lib/didww_ups.rb +118 -0
- data/lib/didww_ups/base.rb +43 -0
- data/lib/didww_ups/collection.rb +17 -0
- data/lib/didww_ups/connection.rb +14 -0
- data/lib/didww_ups/credit_card.rb +155 -0
- data/lib/didww_ups/modules_base.rb +27 -0
- data/lib/didww_ups/modules_payment.rb +18 -0
- data/lib/didww_ups/modules_refund.rb +17 -0
- data/lib/didww_ups/modules_token.rb +14 -0
- data/lib/didww_ups/payment.rb +80 -0
- data/lib/didww_ups/railtie.rb +7 -0
- data/lib/didww_ups/refund.rb +65 -0
- data/lib/didww_ups/store.rb +119 -0
- data/lib/didww_ups/version.rb +3 -0
- data/lib/generators/config.rb +15 -0
- data/lib/generators/templates/didww_ups.yml +14 -0
- data/spec/credit_card_spec.rb +100 -0
- data/spec/didww_ups.yml +5 -0
- data/spec/payment_spec.rb +127 -0
- data/spec/refund_spec.rb +112 -0
- data/spec/spec_helper.rb +98 -0
- data/spec/store_spec.rb +109 -0
- metadata +178 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Igor Fedoronchuk
|
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,34 @@
|
|
1
|
+
# DidwwUps
|
2
|
+
|
3
|
+
[![Build Status](http://img.shields.io/travis/didww/didww_ups.svg)](https://travis-ci.org/didww/didww_ups)
|
4
|
+
[![Coverage Status](http://img.shields.io/coveralls/didww/didww_ups.svg)](https://coveralls.io/r/didww/didww_ups)
|
5
|
+
[![Dependency Status](http://img.shields.io/gemnasium/didww/didww_ups.svg)](https://gemnasium.com/didww/didww_ups)
|
6
|
+
[![Code Climate](http://img.shields.io/codeclimate/github/didww/didww_ups.svg)](https://codeclimate.com/github/didww/didww_ups)
|
7
|
+
[![Gem Version](http://img.shields.io/gem/v/didww_ups.svg)](https://rubygems.org/gems/didww_ups)
|
8
|
+
[![License](http://img.shields.io/:license-mit-blue.svg)](http://didww.mit-license.org)
|
9
|
+
|
10
|
+
|
11
|
+
API Client for DIDWW UPS payment gateway
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
gem 'didww_ups'
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install didww_ups
|
26
|
+
|
27
|
+
|
28
|
+
## Contributing
|
29
|
+
|
30
|
+
1. Fork it
|
31
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
32
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
33
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
34
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/didww_ups.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'didww_ups/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "didww_ups"
|
8
|
+
spec.version = DidwwUps::VERSION
|
9
|
+
spec.authors = ["Igor Fedoronchuk"]
|
10
|
+
spec.email = ["igor.f@didww.com"]
|
11
|
+
spec.description = %q{API client library for DIDWW payment gateway (UPS)}
|
12
|
+
spec.summary = %q{API client library for DIDWW payment gateway (UPS)}
|
13
|
+
spec.homepage = "https://www.didww.com"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency('activeresource-response', "~> 1.1.1")
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "rack"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3"
|
27
|
+
spec.add_development_dependency "webmock"
|
28
|
+
end
|
data/lib/didww_ups.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
require "didww_ups/version"
|
2
|
+
require "logger"
|
3
|
+
require "activeresource-response"
|
4
|
+
require 'active_support/core_ext/module'
|
5
|
+
|
6
|
+
module DidwwUps
|
7
|
+
|
8
|
+
SANDBOX_HOST = 'dev-payments.didww.com'
|
9
|
+
|
10
|
+
PRODUCTION_HOST = 'ups.didww.com'
|
11
|
+
|
12
|
+
mattr_accessor :service_host
|
13
|
+
mattr_accessor :config
|
14
|
+
mattr_accessor :logger
|
15
|
+
mattr_accessor :global_store
|
16
|
+
self.logger = ::Logger.new($stdout)
|
17
|
+
|
18
|
+
def self.configure(env)
|
19
|
+
@configuration_env = env
|
20
|
+
all_configs = load_configuration
|
21
|
+
|
22
|
+
|
23
|
+
if all_configs.any?
|
24
|
+
self.config = all_configs[env].symbolize_keys
|
25
|
+
if config[:ups_host].present?
|
26
|
+
self.service_host = config[:ups_host]
|
27
|
+
else
|
28
|
+
self.environment = config[:ups_env]
|
29
|
+
end
|
30
|
+
DidwwUps::Base.site = 'https://' + self.service_host
|
31
|
+
store = DidwwUps::Store.new(api_public_key: config[:store_public_key], api_secret_key: config[:store_secret_key])
|
32
|
+
DidwwUps.global_store = store
|
33
|
+
DidwwUps.store = store
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def self.environment=(env)
|
39
|
+
env = env.to_sym
|
40
|
+
if [:live, :production].include? env
|
41
|
+
self.service_host = PRODUCTION_HOST
|
42
|
+
elsif [:test, :development, :sandbox].include? env
|
43
|
+
self.service_host = SANDBOX_HOST
|
44
|
+
else
|
45
|
+
raise ArgumentError.new("environment= #{env} accepts any of :live, :production, :test, :sandbox")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.current_store
|
50
|
+
Thread.current['didww_ups.active_store'] || DidwwUps.global_store
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.profile
|
54
|
+
current_store.class.profile
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
def self.store=(store)
|
59
|
+
Thread.current['didww_ups.active_store'] = store
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.with_configuration(new_env)
|
63
|
+
old_env = @configuration_env
|
64
|
+
begin
|
65
|
+
self.configure(new_env)
|
66
|
+
yield if block_given?
|
67
|
+
ensure
|
68
|
+
self.configure(old_env)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.with_store(new_store)
|
73
|
+
old_store = current_store
|
74
|
+
begin
|
75
|
+
self.store = new_store
|
76
|
+
yield if block_given?
|
77
|
+
ensure
|
78
|
+
self.store=old_store
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.load_configuration
|
83
|
+
config_file = ENV['DIDWW_UPS_CONFIG_FILE'] || 'config/didww_ups.yml'
|
84
|
+
if File.exist?(config_file)
|
85
|
+
YAML.load_file(config_file)
|
86
|
+
else
|
87
|
+
raise StandardError.new("File #{config_file} doesn't exist") if ENV['DIDWW_UPS_CONFIG_FILE'].present?
|
88
|
+
logger.warn "Can't find #{config_file}"
|
89
|
+
{}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
require "didww_ups/connection"
|
98
|
+
require "didww_ups/collection"
|
99
|
+
require "didww_ups/base"
|
100
|
+
require "didww_ups/store"
|
101
|
+
require "didww_ups/credit_card"
|
102
|
+
require "didww_ups/payment"
|
103
|
+
require "didww_ups/refund"
|
104
|
+
require "didww_ups/modules_base"
|
105
|
+
require "didww_ups/modules_token"
|
106
|
+
require "didww_ups/modules_payment"
|
107
|
+
require "didww_ups/modules_refund"
|
108
|
+
require 'yaml'
|
109
|
+
if defined? Rails
|
110
|
+
require 'didww_ups/railtie'
|
111
|
+
require 'generators/config'
|
112
|
+
else
|
113
|
+
DidwwUps.configure(ENV['RACK_ENV'])
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
|
118
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# DidwwUps::Base class base class for API resources
|
2
|
+
module DidwwUps
|
3
|
+
class Base < ActiveResource::Base
|
4
|
+
|
5
|
+
self.include_format_in_path = false
|
6
|
+
self.prefix = "/api/rest/v1/"
|
7
|
+
add_response_method :http
|
8
|
+
|
9
|
+
self.collection_parser = DidwwUps::Collection
|
10
|
+
|
11
|
+
delegate :[], to: :@attributes
|
12
|
+
|
13
|
+
def remote_errors_response
|
14
|
+
instance_variable_get(:@remote_errors).try(:response)
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
|
19
|
+
def site=(site)
|
20
|
+
site_uri = create_site_uri_from(site)
|
21
|
+
@site = site_uri
|
22
|
+
Thread.current['didww_ups.site'] = site_uri
|
23
|
+
end
|
24
|
+
|
25
|
+
def site
|
26
|
+
Thread.current['didww_ups.site'] || @site
|
27
|
+
end
|
28
|
+
|
29
|
+
#no headers
|
30
|
+
def headers
|
31
|
+
{}
|
32
|
+
end
|
33
|
+
|
34
|
+
#override with needed Connection class
|
35
|
+
def connection(refresh = false)
|
36
|
+
@connection = DidwwUps::Connection.new(DidwwUps::Base.site, format) if refresh || @connection.nil?
|
37
|
+
@connection.timeout = timeout if timeout
|
38
|
+
@connection.ssl_options = ssl_options if ssl_options
|
39
|
+
@connection
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# DidwwUps::Collection class with ability to handle :total_count, :offset_value, :limit_value
|
2
|
+
module DidwwUps
|
3
|
+
class Collection < ActiveResource::Collection
|
4
|
+
|
5
|
+
attr_reader :total_count, :offset_value, :limit_value
|
6
|
+
|
7
|
+
def resource_class=(rc)
|
8
|
+
super rc
|
9
|
+
response = rc.connection.http_response
|
10
|
+
@total_count = response['X-Total-Count'].to_i
|
11
|
+
@offset_value = response['X-Offset-Value'].to_i
|
12
|
+
@limit_value = response['X-Limit-Value'].to_i
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# DidwwUps::Connection class with overridden authorization_header method
|
2
|
+
module DidwwUps
|
3
|
+
class Connection < ActiveResource::Connection
|
4
|
+
|
5
|
+
|
6
|
+
# Adding Auth header
|
7
|
+
# Example
|
8
|
+
# Token token=dGVzdC1wdWJsaWM0OmNlNTM0M2NiNTY1ZDM3ZmFiZjExZjhkODAzMTA4YmQwMTBkYzc2ZGE=, date=2012-12-03 14:55:57,type=v1
|
9
|
+
def authorization_header(http_method, uri)
|
10
|
+
{'Authorization' => 'Token token=' + DidwwUps.current_store.request_token(URI(uri).request_uri, http_method)}
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# DidwwUps::CreditCard class to handle Credit Cards
|
2
|
+
#
|
3
|
+
# Routes:
|
4
|
+
# GET /api/rest/v1/credit_cards/:id/modules(.:format)
|
5
|
+
# GET /api/rest/v1/credit_cards/:credit_card_id/properties(.:format)
|
6
|
+
# GET /api/rest/v1/credit_cards/:credit_card_id/billing_address(.:format)
|
7
|
+
# GET /api/rest/v1/credit_cards(.:format)
|
8
|
+
# GET /api/rest/v1/credit_cards/:id(.:format)
|
9
|
+
# DELETE /api/rest/v1/credit_cards/:id(.:format)
|
10
|
+
#
|
11
|
+
# Filter Examples
|
12
|
+
# credit_cards = DidwwUps::CreditCard.all(params: { page:1, per_page:10 , q:{token_ref_is_present: true, cc_expired_gte: '2014-06-01'}})
|
13
|
+
# # => GET "/api/rest/v1/credit_cards?page=1&per_page=10&q%5Bcc_number_contains%5D=37"
|
14
|
+
#
|
15
|
+
# Possible filter keys for q hash
|
16
|
+
# * +created_at_gte+
|
17
|
+
# * +created_at_lte+
|
18
|
+
# * +cc_expired_gte+
|
19
|
+
# * +cc_expired_lte+
|
20
|
+
# * +store_id_eq+
|
21
|
+
# * +cc_number_contains+
|
22
|
+
# * +token_ref_contains+
|
23
|
+
# * +token_ref_is_present+ (true)
|
24
|
+
# * +token_ref_is_blank+ (true)
|
25
|
+
# * +customer_id_eq+
|
26
|
+
# * +customer_id_starts_with+
|
27
|
+
#
|
28
|
+
# Methods for pagination
|
29
|
+
# # credit_cards.total_count
|
30
|
+
# # credit_cards.limit_value
|
31
|
+
# # credit_cards.offset_value
|
32
|
+
#
|
33
|
+
# Find by token example
|
34
|
+
# DidwwUps::CreditCard.find("ToTXNnMAUika3o0sS0sqMw")
|
35
|
+
# # => GET "/api/rest/v1/credit_cards/ToTXNnMAUika3o0sS0sqMw"
|
36
|
+
#
|
37
|
+
# Remove example
|
38
|
+
# DidwwUps::CreditCard.new({token_ref:"ToTXNnMAUika3o0sS0sqMw"}, true).destroy
|
39
|
+
# # => DELETE "/api/rest/v1/credit_cards/ToTXNnMAUika3o0sS0sqMw"
|
40
|
+
#
|
41
|
+
#
|
42
|
+
module DidwwUps
|
43
|
+
class CreditCard < DidwwUps::Base
|
44
|
+
|
45
|
+
self.prefix = "/api/rest/v1/"
|
46
|
+
|
47
|
+
validate do
|
48
|
+
unless persisted? # was initialized by DidwwUps::Store#retrieve_credit_card
|
49
|
+
self.errors[:base] << "Invalid callback hash" unless self["secure_hash"].to_s == valid_callback_hash
|
50
|
+
self.errors[:base] << self["error"] if self["error"].present?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
schema do
|
55
|
+
attribute :credit_card_number, :string
|
56
|
+
attribute :credit_card_type, :string
|
57
|
+
attribute :credit_card_expired, :string
|
58
|
+
attribute :unique_id, :string
|
59
|
+
attribute :store_id, :integer
|
60
|
+
attribute :token_ref, :string
|
61
|
+
attribute :updated_at, :datetime
|
62
|
+
attribute :customer_id, :string
|
63
|
+
attribute :card_id, :string
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def updated_at
|
69
|
+
Time.parse(self[:updated_at])
|
70
|
+
end
|
71
|
+
|
72
|
+
def initialize(attributes = {}, persisted = false)
|
73
|
+
super attributes, persisted #check attributes keys
|
74
|
+
if self['success'].to_i == 1
|
75
|
+
@attributes['token_ref'] = self['result']
|
76
|
+
else
|
77
|
+
@attributes['error'] = self['result']
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Properties
|
82
|
+
# DidwwUps::CreditCard.new({:token_ref => "LQ8IQ4Av6hM3tgX_WLJp9w"}, true).get(:properties)
|
83
|
+
# DidwwUps::CreditCard.find("LQ8IQ4Av6hM3tgX_WLJp9w").properties
|
84
|
+
# # => GET "/api/rest/v1/credit_cards/LQ8IQ4Av6hM3tgX_WLJp9w/properties"
|
85
|
+
# # => {"bin"=>"510510", "bank"=>"BANK OF HAWAII", "country"=>"UNITED STATES", "card_brand"=>"MASTERCARD", "card_type"=>"CREDIT", "bank_phone"=>"1-888-643-3888 OR 1-888-643-9888", "customer_ip"=>"195.138.65.189", "fraud_level"=>nil}
|
86
|
+
#
|
87
|
+
def properties
|
88
|
+
attribute_or_get! :properties
|
89
|
+
end
|
90
|
+
|
91
|
+
# Payment Modules
|
92
|
+
# DidwwUps::CreditCard.find("LQ8IQ4Av6hM3tgX_WLJp9w").get(:modules)
|
93
|
+
# DidwwUps::CreditCard.find("LQ8IQ4Av6hM3tgX_WLJp9w").modules
|
94
|
+
# # => GET "/api/rest/v1/credit_cards/LQ8IQ4Av6hM3tgX_WLJp9w/modules"
|
95
|
+
# # => ["WorldnetTps"]
|
96
|
+
def modules
|
97
|
+
self[:modules] ||= self[:modules_tokens].present? ?
|
98
|
+
self[:modules_tokens].collect { |t| t.module_name } :
|
99
|
+
get(:modules)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Billing Address
|
103
|
+
# DidwwUps::CreditCard.new({:token_ref => "LQ8IQ4Av6hM3tgX_WLJp9w"}, true).get(:billing_address)
|
104
|
+
# DidwwUps::CreditCard.find("LQ8IQ4Av6hM3tgX_WLJp9w").billing_address
|
105
|
+
# # => GET "/api/rest/v1/credit_cards/LQ8IQ4Av6hM3tgX_WLJp9w/modules"
|
106
|
+
# # => "{\"bill_first_name\":\"FirstName\",\"bill_last_name\":\"SecondName\",\"city\":\"Odessa\",\"company\":\"\",\"country\":\"UA\",\"created_at\":\"2014-03-27T12:29:43Z\",\"email\":\"olga@gmail.com\",\"fax_number\":\"\",\"first_address_line\":\"Address line1\",\"id\":1070,\"phone_number\":\"3806600000000\",\"second_address_line\":\"Address line2\",\"state\":null,\"updated_at\":\"2014-03-27T12:29:43Z\",\"zip\":\"65007\",\"avs_result\":null}"
|
107
|
+
def billing_address
|
108
|
+
attribute_or_get! :billing_address
|
109
|
+
end
|
110
|
+
|
111
|
+
# create Payment ,
|
112
|
+
# * *See* : DidwwUps::Payment#create
|
113
|
+
# * *Returns* : DidwwUps::Payment
|
114
|
+
def create_payment(params={})
|
115
|
+
DidwwUps::Payment.create(params.merge({token_ref: self['token_ref']}))
|
116
|
+
end
|
117
|
+
|
118
|
+
def to_param
|
119
|
+
token_ref
|
120
|
+
end
|
121
|
+
|
122
|
+
alias :id :to_param
|
123
|
+
|
124
|
+
def brand
|
125
|
+
self[:credit_card_type]
|
126
|
+
end
|
127
|
+
|
128
|
+
def display_name
|
129
|
+
"#{self.brand} #{self[:credit_card_number]}"
|
130
|
+
end
|
131
|
+
|
132
|
+
def expired_this_month?
|
133
|
+
self[:credit_card_expired] == DateTime.now.utc.strftime("%Y-%d")
|
134
|
+
end
|
135
|
+
|
136
|
+
def expired?
|
137
|
+
self[:credit_card_expired] < DateTime.now.utc.strftime("%Y-%d")
|
138
|
+
end
|
139
|
+
|
140
|
+
protected
|
141
|
+
def valid_callback_hash
|
142
|
+
Digest::SHA1.hexdigest "#{self['result']}#{self['success']}#{self['customer_id']}#{self['card_id']}#{self['date_time']}#{DidwwUps.current_store['api_secret_key']}"
|
143
|
+
end
|
144
|
+
|
145
|
+
def attribute_or_get!(name)
|
146
|
+
if self.attributes.has_key? name
|
147
|
+
return {} if self[name].nil?
|
148
|
+
self[name].is_a?(Hash) ? self[name] : self[name].try(:attributes)
|
149
|
+
else
|
150
|
+
self[name] = get(name)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|