didww_ups 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.
@@ -0,0 +1,27 @@
1
+ # DidwwUps::Base class base class for API resources
2
+ module DidwwUps
3
+ module ModulesBase
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ self.prefix = "/api/rest/v1/logs/:module_name/"
9
+
10
+ def module_name
11
+ prefix_options[:module_name]
12
+ end
13
+
14
+ def duplicable?
15
+ false
16
+ end
17
+
18
+ def created_at
19
+ Time.parse(self[:created_at])
20
+ end
21
+
22
+
23
+ end
24
+
25
+
26
+ end
27
+ end
@@ -0,0 +1,18 @@
1
+ # Routes: /api/rest/v1/logs/:type/tokens(.:format)
2
+ # Example
3
+ # DidwwUps::ModulesPayment.all(:params=> {:module_name=>'Braintree'})
4
+ # #=> "[{"amount":"2054.0","created_at":"2014-03-13T14:04:06Z","id":134,"store_id":14,"success":false,"module_name":"Braintree","error":"Reversal amount does not match authorization amount"},{"amount":"42.0","created_at":"2014-03-13T14:01:08Z","id":133,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"16.0","created_at":"2014-03-13T13:56:29Z","id":132,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"91.0","created_at":"2014-03-13T13:27:56Z","id":131,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"10.0","created_at":"2014-03-13T13:24:07Z","id":130,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"10.0","created_at":"2014-03-13T13:21:38Z","id":129,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"10.0","created_at":"2014-03-13T13:15:26Z","id":128,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"10.0","created_at":"2014-03-13T13:13:34Z","id":127,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"10.0","created_at":"2014-03-13T13:09:32Z","id":126,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"50.0","created_at":"2014-03-13T12:57:57Z","id":125,"store_id":14,"success":true,"module_name":"Braintree","error":null}]"
5
+ #
6
+
7
+ module DidwwUps
8
+ class ModulesPayment < DidwwUps::Base
9
+ include DidwwUps::ModulesBase
10
+ self.element_name = 'payment'
11
+
12
+ def success
13
+ !!attributes[:success]
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,17 @@
1
+ # Routes: /api/rest/v1/logs/:type/tokens(.:format)
2
+ # Example
3
+ # DidwwUps::ModulesRefund.all(params: {module_name: "Braintree", :order => "created_at_desc"})
4
+ # #=>"[{"amount":"34.91","created_at":"2013-12-17T14:09:14Z","id":31,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"12.0","created_at":"2013-12-13T15:07:28Z","id":29,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"10.0","created_at":"2013-12-07T09:07:28Z","id":28,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"23.0","created_at":"2013-12-05T17:05:03Z","id":27,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"50.0","created_at":"2013-12-05T12:40:11Z","id":26,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"31.21","created_at":"2013-12-05T10:09:17Z","id":25,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"12.0","created_at":"2013-12-05T09:53:53Z","id":24,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"12.0","created_at":"2013-12-05T09:27:31Z","id":23,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"34.0","created_at":"2013-11-18T13:55:28Z","id":15,"store_id":14,"success":true,"module_name":"Braintree","error":null},{"amount":"15.0","created_at":"2013-11-11T09:05:22Z","id":14,"store_id":14,"success":true,"module_name":"Braintree","error":null}]"
5
+ #
6
+
7
+ module DidwwUps
8
+ class ModulesRefund < DidwwUps::Base
9
+ include DidwwUps::ModulesBase
10
+ self.element_name = 'refund'
11
+
12
+
13
+ def success
14
+ !!attributes[:success]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ # Routes: /api/rest/v1/logs/:type/tokens(.:format)
2
+ # Example
3
+ # DidwwUps::ModulesToken.all(params: {module_name: "Braintree", :order => "created_at_desc"})
4
+
5
+ module DidwwUps
6
+ class ModulesToken < DidwwUps::Base
7
+ include DidwwUps::ModulesBase
8
+ self.element_name = 'token'
9
+
10
+ def success
11
+ !!attributes[:success?]
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,80 @@
1
+ # DidwwUps::Payment class to handle Payments
2
+ #
3
+ # Routes:
4
+ # GET /api/rest/v1/payments(.:format)
5
+ # POST /api/rest/v1/payments(.:format)
6
+ #
7
+ # Filter Examples
8
+ # payments = DidwwUps::Payment.all(params: { page:1, per_page:10 , q:{success_eq: true}})
9
+ # # => GET "/api/rest/v1/payments?page=1&per_page=10&q%5Bsuccess_eq%5D=true"
10
+ #
11
+ # Methods for pagination
12
+ # # payments.total_count
13
+ # # payments.limit_value
14
+ # # payments.offset_value
15
+ #
16
+ #
17
+ # Possible filter keys for params[:q]
18
+ # * +created_at_gte+
19
+ # * +created_at_lte+
20
+ # * +amount_eq+
21
+ # * +cc_number_contains+
22
+ # * +order_id_eq+
23
+ # * +success_eq+
24
+ # * +store_id_eq+
25
+ # * +customer_id_eq+
26
+ #
27
+ # Create Payment
28
+ #
29
+ # Example failed by merchant
30
+ #
31
+ # payment = DidwwUps::Payment.create({token_ref: "LQ8IQ4Av6hM3tgX_WLJp9w", order_id: 7, amount: 20.01, operator: 'Admin', description: 'Payment for order # 7'})
32
+ # # => POST "/api/rest/v1/payments" "{\"order_id\":7,\"amount\":20.1,\"operator\":\"Admin\",\"description\":\"Payment for order # 7\",\"token_ref\":\"LQ8IQ4Av6hM3tgX_WLJp9w\"}"
33
+ # # => "{\"id\":1967,\"token_ref\":\"LQ8IQ4Av6hM3tgX_WLJp9w\",\"cc_type\":\"MASTERCARD\",\"cc_number\":\"540400******0001\",\"success\":false,\"error\":\"Declined\",\"amount\":20.5,\"order_id\":7,\"description\":\"Payment for order # 7\",\"operator\":\"Admin\",\"type\":\"WorldnetTps\"}"
34
+ # payment.valid? # => true
35
+ # payment.success? # => false
36
+ #
37
+ # Example failed by UPS
38
+ #
39
+ # payment = DidwwUps::Payment.create({token_ref: "LQ8IQ4Av6hM3tgX_WLJp9w", order_id: 6, amount: 20.60, operator: 'Admin', description: 'Payment for order # 6'})
40
+ # # => "{\"errors\":{\"base\":[\"Order was already processed\"]}}"
41
+ # payment.success? # => false
42
+ # payment.valid? # => false
43
+ # errors.full_messages # => ["Order was already processed"]
44
+ #
45
+ # Example successfull
46
+ #
47
+ # payment = DidwwUps::Payment.create({token_ref: "LQ8IQ4Av6hM3tgX_WLJp9w",order_id: 8, amount: 20.50, operator: 'Admin', description: 'Payment for order # 8'})
48
+ # # => "{\"id\":1968,\"token_ref\":\"LQ8IQ4Av6hM3tgX_WLJp9w\",\"cc_type\":\"MASTERCARD\",\"cc_number\":\"540400******0001\",\"success\":true,\"error\":null,\"amount\":20.5,\"order_id\":8,\"description\":\"Payment for order # 8\",\"operator\":\"Admin\",\"type\":\"WorldnetTps\"}"
49
+ # payment.success? # => true
50
+ # payment.valid? # => true
51
+ #
52
+ #
53
+ #
54
+ module DidwwUps
55
+ class Payment < DidwwUps::Base
56
+
57
+ def created_at
58
+ Time.parse(self[:created_at])
59
+ end
60
+
61
+ def success?
62
+ valid? && persisted? && success
63
+ end
64
+
65
+ #
66
+ # Creates refund from payment
67
+ #
68
+ # Params
69
+ # * +:amount+ - amount of refund, if not present payment amount is used
70
+ # * +:description+ - Description of refund, if not present payment description is used
71
+ # * +:operator+ - responsible for refund, if not present payment operator is used
72
+ #
73
+ #
74
+ def refund(params={})
75
+ params = {amount: self.amount, description: self.description, operator: self.operator}.merge(params).merge({order_id: self.order_id})
76
+ DidwwUps::Refund.create(params)
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,7 @@
1
+ module DidwwUps
2
+ class Railtie < ::Rails::Railtie
3
+ config.after_initialize do
4
+ DidwwUps.configure(Rails.env)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,65 @@
1
+ # DidwwUps::Refund class to handle Refunds
2
+ #
3
+ # Routes:
4
+ # GET /api/rest/v1/refunds(.:format)
5
+ # POST /api/rest/v1/refunds(.:format)
6
+ #
7
+ # Filter Examples
8
+ # refunds = DidwwUps::Refund.all(params: { page:1, per_page:10 , q:{success_eq: true}})
9
+ # # => GET "/api/rest/v1/refunds?page=1&per_page=10&q%5Bsuccess_eq%5D=true"
10
+ #
11
+ # Methods for pagination
12
+ # # refunds.total_count
13
+ # # refunds.limit_value
14
+ # # refunds.offset_value
15
+ #
16
+ #
17
+ # Possible filter keys for params[:q]
18
+ # * +:created_at_gte+
19
+ # * +:created_at_lte+
20
+ # * +:store_id_eq+
21
+ # * +:amount_eq+
22
+ # * +:description_contains+
23
+ # * +:operator_contains+
24
+ # * +:order_id_eq+
25
+ # * +:success_eq+
26
+ #
27
+ # Create Refund
28
+ #
29
+ # Example failed by merchant
30
+ # refund = DidwwUps::Refund.create(order_id: "1294526", amount:12.00, operator: "Bob", description: "Stolen Card")
31
+ # # => "{\"id\":466,\"success\":false,\"Credit Card is expired\":null,\"amount\":12.0,\"order_id\":\"1294526\",\"description\":\"Stolen Card\",\"operator\":\"Bob\"}"
32
+ # refund.valid? # => true
33
+ # refund.success? # => false
34
+ #
35
+ # Example failed by UPS
36
+ # refund = DidwwUps::Refund.create(order_id: "1294526", amount:1000.00, operator: "Bob", description: "Stolen Card")
37
+ # # => "{\"errors\":{\"amount\":[\"must be less or equal to 14.0\"]}}"
38
+ # refund.success? # => false
39
+ # refund.valid? # => false
40
+ # errors.full_messages # => ["Amount must be less or equal to 14.0"]
41
+ #
42
+ # Example successfull
43
+ # refund = DidwwUps::Refund.create(order_id: "1294526", amount:12.00, operator: "Bob", description: "Stolen Card")
44
+ # # => "{\"id\":466,\"success\":true,\"error\":null,\"amount\":12.0,\"order_id\":\"1294526\",\"description\":\"Stolen Card\",\"operator\":\"Bob\"}"
45
+ # refund.success? # => true
46
+ # refund.valid? # => true
47
+ #
48
+ # Params
49
+ # * +:amount+ - amount of refund, if not present payment amount is used
50
+ # * +:description+ - Description of refund, if not present payment description is used
51
+ # * +:operator+ - responsible for refund, if not present payment operator is used
52
+ # * +:order_id+ - order_id of payment
53
+ #
54
+ module DidwwUps
55
+ class Refund < DidwwUps::Base
56
+
57
+ def created_at
58
+ Time.parse(self[:created_at])
59
+ end
60
+
61
+ def success?
62
+ valid? && persisted? && success
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,119 @@
1
+ # DidwwUps::Store class with Store used as API client
2
+ module DidwwUps
3
+ class Store < DidwwUps::Base
4
+
5
+ NEW_CREDIT_CARD_PATH = '/api/iframe/new'
6
+ APPEND_CREDIT_CARD_PATH = '/api/iframe/append'
7
+
8
+ class InvalidRequest < StandardError
9
+
10
+ end
11
+
12
+ # Retrieves profile information for current store
13
+ # profile = DidwwUps::Store.profile
14
+ # # => "{\"api_public_key\":\"somekey\",\"api_secret_key\":\"somesecret\",\"callback_url\":\"http://yoursite.com/callback.php\",\"created_at\":\"2012-12-24T11:03:36Z\",\"enabled\":true,\"id\":1,\"ip\":null,\"name\":\"mystore\",\"terminal_id\":1,\"updated_at\":\"2013-01-09T12:27:01Z\",\"details\":{\"terminal_id\":1,\"currency_sign\":\"USD\",\"modules\":[\"Stripe\",\"Braintree\"]}}"
15
+
16
+ def self.profile
17
+ self.get(:profile)
18
+ end
19
+
20
+
21
+ # Builds new uri for customer to enter credit card info
22
+ #
23
+ # * *Returns* : URI::HTTPS object
24
+ # Example
25
+ # <URI::HTTPS:0x007fa62377cfb0 URL:https://google.com/api/iframe/new?customer_id=1&date_time=2014-06-05+21%3A30%3A46&hash=4452d71687b6bc2c9389c3349fdc17fbd73b833b&public_key=public>
26
+ # * +customer_id+ - unique string for customer or customer/credit-card combination
27
+ # * +card_id+ - card id unique for customer_id
28
+ # * +date_time+ - current utc datetime
29
+ # * +public_key+ - public api key number
30
+ # * +hash+ - SHA1 of next string = api_secret_key+date_time+customer_id+ card_id
31
+
32
+ def new_credit_card_uri(customer_id, options = {})
33
+ credit_card_uri(NEW_CREDIT_CARD_PATH, customer_id, options)
34
+ end
35
+
36
+
37
+ def append_credit_card_uri(customer_id, options = {})
38
+ credit_card_uri(APPEND_CREDIT_CARD_PATH, customer_id, options)
39
+ end
40
+
41
+
42
+ def credit_card_uri(path, customer_id, options)
43
+ URI::HTTPS.build(host: DidwwUps.service_host,
44
+ path: path,
45
+ query: credit_card_params(customer_id, options).to_query)
46
+ end
47
+
48
+ def credit_card_params(customer_id, options = {})
49
+ date_time = formatted_date_time
50
+ card_id = options.has_key?(:card_id) ? options.delete(:card_id) : nil
51
+ signed_hash = new_credit_card_uri_hash(customer_id, card_id, date_time)
52
+ options.merge({customer_id: customer_id,
53
+ card_id: card_id,
54
+ date_time: date_time,
55
+ public_key: self['api_public_key'],
56
+ hash: signed_hash })
57
+ end
58
+
59
+ #
60
+ # Build credit card instance from callback uri params
61
+ #
62
+ # Params
63
+ # * +success+ - 0/1 (1 - YES)
64
+ # * +credit_card_expired+ - (credit card expiration YEAR-month)
65
+ # * +credit_card_number+ - (masked card number)
66
+ # * +credit_card_type+ - (credit card type upcased)
67
+ # * +customer_id+ - (customer id, that opened the iframe - check if currently logged in )
68
+ # * +date_time+ - UTC datetime (2014-06-06:09:19:16:000)
69
+ # * +result+ - =error string if success equal to 0 , credit card ups token other-way.
70
+ # * +secure_hash+ - SHA1 of result + success + customer_id + date_time + api_secret_key
71
+ #
72
+ # Example success
73
+ # #card_id=1&credit_card_expired=2020-06&credit_card_number=540400%2A%2A%2A%2A%2A%2A0001&credit_card_type=MASTERCARD&customer_id=test&date_time=2014-06-06%3A09%3A19%3A16%3A000&result=4UHSmRkBMhWtHloKQfkjVQ&secure_hash=e4817a8fdb5885248c5f6c6044f4c8707016f013&store=worldnet&success=1
74
+ # Example failed
75
+ # #credit_card_expired=&credit_card_number=&credit_card_type=&customer_id=1&date_time=2014-06-06%3A08%3A12%3A35%3A000&result=AVS+FAILURE&secure_hash=b5d63a10ca10e086dd3e087975b7eff4883f0216&success=0
76
+ def retrieve_credit_card(params= {})
77
+
78
+ DidwwUps::CreditCard.new({
79
+ result: params["result"],
80
+ customer_id: params['customer_id'],
81
+ credit_card_expired: params['credit_card_expired'],
82
+ credit_card_type: params['credit_card_type'],
83
+ store_id: self.id,
84
+ credit_card_number: params['credit_card_number'],
85
+ secure_hash: params['secure_hash'],
86
+ date_time: params['date_time'],
87
+ success: params['success'],
88
+ card_id: params['card_id']
89
+ })
90
+
91
+ end
92
+
93
+ # raises InvalidRequest
94
+ def retrieve_credit_card!(params)
95
+ credit_card = retrieve_credit_card(params)
96
+ raise InvalidRequest.new(credit_card.errors.full_messages.join(",")) unless credit_card.valid?
97
+ credit_card
98
+ end
99
+
100
+ # method to generate authorization header token value
101
+ def request_token(path, method)
102
+ date_time = formatted_date_time
103
+
104
+ sign = Digest::SHA1.hexdigest "#{self['api_secret_key']}#{date_time}#{method.to_s.upcase}#{path}"
105
+ "#{Base64.urlsafe_encode64("#{self['api_public_key']}:#{sign}").strip}, date_time=#{date_time},type=v1"
106
+ end
107
+
108
+ def new_credit_card_uri_hash(customer_id, card_id, date_time)
109
+ Digest::SHA1.hexdigest(self['api_secret_key'] + date_time + customer_id.to_s + card_id.to_s)
110
+ end
111
+
112
+ protected
113
+
114
+ def formatted_date_time
115
+ Time.now.utc.strftime("%F %T")
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,3 @@
1
+ module DidwwUps
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,15 @@
1
+ require 'rails/generators'
2
+ module DidwwUps
3
+ class ConfigGenerator < Rails::Generators::Base
4
+ desc "Generates didww_ups.yml config file in application config directory"
5
+
6
+ def self.source_root
7
+ @source_root ||= File.join(File.dirname(__FILE__), 'templates')
8
+ end
9
+
10
+ def generate_config
11
+ copy_file "didww_ups.yml", "config/didww_ups.yml"
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ production:
2
+ ups_env: 'sandbox'
3
+ store_public_key: 'production-publickey'
4
+ store_secret_key: 'production-secret'
5
+
6
+ development:
7
+ ups_env: 'sandbox'
8
+ store_public_key: 'development-publickey'
9
+ store_secret_key: 'development-secretkey'
10
+
11
+ test:
12
+ ups_env: 'sandbox'
13
+ store_public_key: 'test-publickey'
14
+ store_secret_key: 'test-secretkey'
@@ -0,0 +1,100 @@
1
+ require 'spec_helper'
2
+ describe DidwwUps::CreditCard do
3
+ before do
4
+
5
+ @path = DidwwUps::CreditCard.site.to_s + DidwwUps::CreditCard.prefix.to_s
6
+ @credit_card = DidwwUps::CreditCard.new({
7
+ token_ref: 'LQ8IQ4Av6hM3tgX_WLJp9w'
8
+ }, true)
9
+ end
10
+
11
+ context "Has properties" do
12
+ before do
13
+ @properties = {
14
+ bin: "510510",
15
+ bank: "BANK OF HAWAII",
16
+ country: "UNITED STATES",
17
+ card_brand: "MASTERCARD",
18
+ card_type: "CREDIT",
19
+ bank_phone: "1-888-643-3888 OR 1-888-643-9888",
20
+ customer_ip: "195.138.65.189",
21
+ fraud_level: nil}
22
+
23
+ properties_uri_template = Addressable::Template.new @path + 'credit_cards/{id}/properties'
24
+
25
+
26
+ stub_request(:get, properties_uri_template).to_return(body: @properties.to_json)
27
+
28
+ end
29
+
30
+ it "should respond to #properties and return credit card's properities" do
31
+ expect(@credit_card.properties).to eq(@properties.stringify_keys)
32
+ end
33
+
34
+ end
35
+
36
+ context "Has billing address" do
37
+ before do
38
+ @billing_address = {
39
+ bill_first_name: "FirstName",
40
+ bill_last_name: "SecondName",
41
+ city: "Odessa",
42
+ company: "",
43
+ country: "UA",
44
+ created_at: "2014-03-27T12:29:43Z",
45
+ email: "olga@gmail.com",
46
+ fax_number: "",
47
+ first_address_line: "Address line1",
48
+ id: 1070,
49
+ phone_number: "3806600000000",
50
+ second_address_line: "Address line2",
51
+ state: nil,
52
+ updated_at: "2014-03-27T12:29:43Z",
53
+ zip: "65007",
54
+ avs_result: nil
55
+ }
56
+
57
+ address_uri_template = Addressable::Template.new @path + 'credit_cards/{id}/billing_address'
58
+ stub_request(:get, address_uri_template).to_return(body: @billing_address.to_json)
59
+ end
60
+ it "should respond to #billign_address and return credit card's billing address" do
61
+ expect(@credit_card.billing_address).to eq(@billing_address.stringify_keys)
62
+ end
63
+ end
64
+
65
+ context "Has modules" do
66
+
67
+ before do
68
+ @modules = ['Stripe', 'Braintree']
69
+
70
+ modules_uri_template = Addressable::Template.new @path + 'credit_cards/{id}/modules'
71
+ stub_request(:get, modules_uri_template).to_return(body: @modules.to_json)
72
+ end
73
+
74
+ it "should respond to #modules and return credit card's modules" do
75
+ expect(@credit_card.modules).to eq(@modules)
76
+ end
77
+ end
78
+
79
+
80
+ context "Read Collection" do
81
+ before do
82
+ @collection = [
83
+ {token_ref: 'LQ8IQ4Av6hM3tgX_WLJp9w', credit_card_number: '555555******4444', credit_card_type: 'MASTERCARD', credit_card_expired: '2014-05', store_id: 1, customer_id: 1},
84
+ {token_ref: 'LQ8IQ4Av6hM3tgX_WLJp9E', credit_card_number: '401288******1881', credit_card_type: 'VISA', credit_card_expired: '2015-09', store_id: 1, customer_id: 2}
85
+ ]
86
+ @collection_headers = {'X-Total-Count' => '10', 'X-Offset-Value' => '0', 'X-Limit-Value' => '2'}
87
+ stub_request(:get, @path + 'credit_cards?page=1&per_page=2').to_return(body: @collection.to_json, headers: @collection_headers)
88
+ end
89
+
90
+ it "should be success" do
91
+ credit_cards = DidwwUps::CreditCard.all(params: {page: 1, per_page: 2})
92
+ expect(credit_cards.count).to eq(2)
93
+ expect(credit_cards.total_count).to eq(@collection_headers['X-Total-Count'].to_i)
94
+ expect(credit_cards.offset_value).to eq(@collection_headers['X-Offset-Value'].to_i)
95
+ expect(credit_cards.limit_value).to eq(@collection_headers['X-Limit-Value'].to_i)
96
+
97
+ end
98
+ end
99
+
100
+ end