didww_ups 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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