adaptive_pay 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,120 @@
1
+ require "net/http"
2
+ require "net/https"
3
+ require 'bigdecimal/util'
4
+
5
+ module AdaptivePay
6
+ class Request
7
+
8
+ def self.response_type
9
+ :other
10
+ end
11
+
12
+ class_inheritable_accessor :attributes
13
+ self.attributes = []
14
+
15
+ def self.attribute(name, options={})
16
+ top_level_name = name.to_s.split(".").last.underscore
17
+ define_method top_level_name do
18
+ read_attribute name
19
+ end
20
+
21
+ define_method "#{top_level_name}=" do |value|
22
+ write_attribute name, value
23
+ end
24
+
25
+ self.attributes << options.merge(:name => name)
26
+ end
27
+
28
+ def self.attribute_names
29
+ self.attributes.map { |a| a[:name] }
30
+ end
31
+
32
+
33
+ def initialize(&block)
34
+ @attributes = {}
35
+ self.class.attributes.each do |k|
36
+ write_attribute k[:name], k[:default] if k[:default]
37
+ end
38
+ yield self if block_given?
39
+ end
40
+
41
+ def read_attribute(name)
42
+ @attributes[name]
43
+ end
44
+
45
+ def format_attribute(name)
46
+ attrib = self.class.attributes.find { |a| a[:name] == name }
47
+ case
48
+ when attrib.nil?
49
+ nil
50
+ when attrib[:format] == :date
51
+ @attributes[name].strftime("%Y-%m-%d")
52
+ else
53
+ @attributes[name]
54
+ end
55
+ end
56
+
57
+ def write_attribute(name, value)
58
+ @attributes[name] = value
59
+ end
60
+
61
+
62
+ def perform(interface)
63
+ uri = construct_uri(interface)
64
+ request = Net::HTTP::Post.new uri.request_uri
65
+ request.body = serialize
66
+ request.initialize_http_header(headers(interface))
67
+ http_response = build_http(uri).request request
68
+ Response.new interface, self.class.response_type, http_response
69
+ end
70
+
71
+ def serialize
72
+ result = []
73
+ all_attributes.each do |k, v|
74
+ result << "#{k}=#{URI.escape(v.to_s)}"
75
+ end
76
+ result.join("&")
77
+ end
78
+
79
+ protected
80
+ def construct_uri(interface)
81
+ if interface.base_url.ends_with?("/")
82
+ URI.parse("#{interface.base_url}#{self.class.endpoint}")
83
+ else
84
+ URI.parse("#{interface.base_url}/#{self.class.endpoint}")
85
+ end
86
+ end
87
+
88
+ def build_http(uri)
89
+ http = Net::HTTP.new(uri.host, uri.port)
90
+ http.use_ssl = (uri.port == 443)
91
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
92
+ http
93
+ end
94
+
95
+ def headers(interface)
96
+ {
97
+ "X-PAYPAL-SECURITY-USERID" => interface.username.to_s,
98
+ "X-PAYPAL-SECURITY-PASSWORD" => interface.password.to_s,
99
+ "X-PAYPAL-SECURITY-SIGNATURE" => interface.signature.to_s,
100
+ "X-PAYPAL-APPLICATION-ID" => interface.application_id.to_s,
101
+ "X-PAYPAL-REQUEST-DATA-FORMAT" => "NV",
102
+ "X-PAYPAL-RESPONSE-DATA-FORMAT" => "NV"
103
+ }
104
+ end
105
+
106
+ def all_attributes
107
+ result = {}
108
+ self.class.attribute_names.each do |name|
109
+ value = read_attribute(name)
110
+ result[name] = format_attribute(name) unless value.nil?
111
+ end
112
+ result.merge(extra_attributes)
113
+ end
114
+
115
+ def extra_attributes
116
+ {}
117
+ end
118
+
119
+ end
120
+ end
@@ -0,0 +1,70 @@
1
+ module AdaptivePay
2
+ class Response
3
+
4
+ def initialize(interface, type, response)
5
+ @type = type
6
+ @base_page_url = interface.base_page_url
7
+ @attributes = {}
8
+ parse response
9
+ end
10
+
11
+ def success?
12
+ !!@success
13
+ end
14
+
15
+ def failure?
16
+ !@success
17
+ end
18
+
19
+
20
+ def created?
21
+ payment_exec_status == "CREATED"
22
+ end
23
+
24
+ def pending?
25
+ payment_exec_status == "PENDING"
26
+ end
27
+
28
+ def completed?
29
+ payment_exec_status == "COMPLETED"
30
+ end
31
+
32
+
33
+ def payment_page_url
34
+ case @type
35
+ when :preapproval
36
+ "#{@base_page_url}/webscr?cmd=_ap-preapproval&preapprovalkey=#{URI.escape(preapproval_key)}"
37
+ when :payment
38
+ "#{@base_page_url}/webscr?cmd=_ap-payment&paykey=#{URI.escape(pay_key)}"
39
+ end
40
+ end
41
+
42
+ def read_attribute(name)
43
+ @attributes[name]
44
+ end
45
+
46
+ def method_missing(name, *args)
47
+ if @attributes.has_key?(name.to_s.camelize(:lower))
48
+ @attributes[name.to_s.camelize(:lower)]
49
+ else
50
+ super
51
+ end
52
+ end
53
+
54
+ protected
55
+ def parse(response)
56
+ unless response.code.to_s == "200"
57
+ @success = false
58
+ return
59
+ end
60
+
61
+ response.body.to_s.split("&").each do |fragment|
62
+ k, v = fragment.split("=")
63
+ @attributes[k] = URI.unescape(v)
64
+ end
65
+
66
+ @success = %w{Success SuccessWithWarning}.include?(read_attribute("responseEnvelope.ack"))
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,13 @@
1
+ module AdaptivePay
2
+ class Sender
3
+
4
+ attr_accessor :email, :client_ip
5
+
6
+ def initialize(options={})
7
+ options.each do |k, v|
8
+ send "#{k}=", v
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/adaptive_pay/interface')
2
+ require File.expand_path(File.dirname(__FILE__) + '/adaptive_pay/request')
3
+ require File.expand_path(File.dirname(__FILE__) + '/adaptive_pay/abstract_payment_request')
4
+ require File.expand_path(File.dirname(__FILE__) + '/adaptive_pay/preapproval_request')
5
+ require File.expand_path(File.dirname(__FILE__) + '/adaptive_pay/payment_request')
6
+ require File.expand_path(File.dirname(__FILE__) + '/adaptive_pay/refund_request')
7
+ require File.expand_path(File.dirname(__FILE__) + '/adaptive_pay/cancel_preapproval_request')
8
+ require File.expand_path(File.dirname(__FILE__) + '/adaptive_pay/response')
9
+ require File.expand_path(File.dirname(__FILE__) + '/adaptive_pay/sender')
10
+ require File.expand_path(File.dirname(__FILE__) + '/adaptive_pay/recipient')
11
+ require File.expand_path(File.dirname(__FILE__) + '/adaptive_pay/callback')
@@ -0,0 +1,41 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe AdaptivePay::AbstractPaymentRequest do
4
+
5
+ before :each do
6
+ @payment_request = AdaptivePay::AbstractPaymentRequest.new
7
+ end
8
+
9
+ describe "sender" do
10
+
11
+ it "should save sender object" do
12
+ @payment_request.sender = AdaptivePay::Sender.new :email => "test@paypal.com", :client_ip => "12.23.34.45"
13
+ @payment_request.sender.should be_a(AdaptivePay::Sender)
14
+ @payment_request.sender.email.should == "test@paypal.com"
15
+ @payment_request.sender.client_ip.should == "12.23.34.45"
16
+ end
17
+
18
+ it "should save sender hash" do
19
+ @payment_request.sender = { :email => "test@paypal.com", :client_ip => "12.23.34.45" }
20
+ @payment_request.sender.should be_a(AdaptivePay::Sender)
21
+ @payment_request.sender.email.should == "test@paypal.com"
22
+ @payment_request.sender.client_ip.should == "12.23.34.45"
23
+ end
24
+
25
+ end
26
+
27
+ describe "serialize" do
28
+
29
+ it "should include sender details in serialization" do
30
+ request = AdaptivePay::AbstractPaymentRequest.new
31
+ request.sender = { :client_ip => "11.22.33.44", :email => "sender@paypal.com" }
32
+ decompose(request.serialize).should == {
33
+ "clientDetails.ipAddress" => "11.22.33.44",
34
+ "senderEmail" => "sender@paypal.com",
35
+ "requestEnvelope.errorLanguage" => "en_US"
36
+ }
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,17 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe AdaptivePay::Callback do
4
+
5
+ it "should parse params" do
6
+ callback = AdaptivePay::Callback.new "txn_id" => "3XC103945N720211C", "invoice" => "923204115", "payment_status" => "Completed"
7
+ callback.txn_id.should == "3XC103945N720211C"
8
+ callback.invoice.should == "923204115"
9
+ callback.payment_status.should == "Completed"
10
+ end
11
+
12
+ it "should identify Completed callback" do
13
+ callback = AdaptivePay::Callback.new "payment_status" => "Completed"
14
+ callback.should be_completed
15
+ end
16
+
17
+ end
@@ -0,0 +1,22 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe AdaptivePay::CancelPreapprovalRequest do
4
+
5
+ before :each do
6
+ @refund_request = AdaptivePay::CancelPreapprovalRequest.new
7
+ end
8
+
9
+ describe "serialize" do
10
+
11
+ it "should include recipient details in serialization" do
12
+ request = AdaptivePay::CancelPreapprovalRequest.new
13
+ request.preapproval_key = "PA-12345"
14
+ decompose(request.serialize).should == {
15
+ "preapprovalKey" => "PA-12345",
16
+ "requestEnvelope.errorLanguage" => "en_US"
17
+ }
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,184 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe AdaptivePay::Interface do
4
+
5
+ before :each do
6
+ AdaptivePay::Interface.requests.clear
7
+ @interface = AdaptivePay::Interface.new false
8
+ @interface.base_url = "https://paypal.yumshare.com/"
9
+ end
10
+
11
+ describe "initialize" do
12
+
13
+ it "should load configuration from adaptive_pay.yml according to" do
14
+ Rails.stub!(:root).and_return(File.expand_path(File.dirname(__FILE__) + '/../fixtures'))
15
+ Rails.stub!(:env).and_return("development")
16
+ interface = AdaptivePay::Interface.new
17
+ interface.environment.should == :sandbox
18
+ interface.base_url.should == "https://svcs.sandbox.paypal.com/AdaptivePayments/"
19
+ interface.username.should == "my_username"
20
+ interface.password.should == "my_password"
21
+ interface.signature.should == "my_signature"
22
+ interface.application_id.should == "my_application_id"
23
+ interface.should_not be_retain_requests_for_test
24
+ end
25
+
26
+ it "should allow setting environment as a parameter" do
27
+ Rails.stub!(:root).and_return(File.expand_path(File.dirname(__FILE__) + '/../fixtures'))
28
+ Rails.stub!(:env).and_return("development")
29
+ interface = AdaptivePay::Interface.new :production
30
+ interface.environment.should == :production
31
+ interface.base_url.should == "https://svcs.paypal.com/AdaptivePayments/"
32
+ interface.username.should == "my_production_username"
33
+ interface.password.should == "my_production_password"
34
+ interface.signature.should == "my_production_signature"
35
+ interface.should_not be_retain_requests_for_test
36
+ end
37
+
38
+ it "should allow preventing the configuration loading" do
39
+ interface = AdaptivePay::Interface.new false
40
+ interface.environment.should be_blank
41
+ interface.base_url.should be_blank
42
+ interface.username.should be_blank
43
+ interface.password.should be_blank
44
+ interface.signature.should be_blank
45
+ end
46
+
47
+ end
48
+
49
+ describe "set_environment" do
50
+
51
+ it "should set production environment" do
52
+ @interface.set_environment :production
53
+ @interface.environment.should == :production
54
+ @interface.base_url.should == "https://svcs.paypal.com/AdaptivePayments/"
55
+ @interface.base_page_url.should == "https://www.paypal.com"
56
+ end
57
+
58
+ it "should set sandbox environment" do
59
+ @interface.set_environment :sandbox
60
+ @interface.environment.should == :sandbox
61
+ @interface.base_url.should == "https://svcs.sandbox.paypal.com/AdaptivePayments/"
62
+ @interface.base_page_url.should == "https://www.sandbox.paypal.com"
63
+ end
64
+
65
+ it "should set beta_sandbox environment" do
66
+ @interface.set_environment :beta_sandbox
67
+ @interface.environment.should == :beta_sandbox
68
+ @interface.base_url.should == "https://svcs.beta-sandbox.paypal.com/AdaptivePayments/"
69
+ @interface.base_page_url.should == "https://www.beta-sandbox.paypal.com"
70
+ end
71
+
72
+ end
73
+
74
+ describe "request_preapproval" do
75
+
76
+ before :each do
77
+ @interface.retain_requests_for_test = true
78
+ end
79
+
80
+ it "should enqueue request if retain_requests_for_test is set" do
81
+ request = nil
82
+ @interface.request_preapproval do |r|
83
+ request = r
84
+ end
85
+ AdaptivePay::Interface.requests.size.should == 1
86
+ AdaptivePay::Interface.requests.first.should == request
87
+ end
88
+
89
+ it "should allow providing pre canned responses" do
90
+ response = mock(:response)
91
+ AdaptivePay::Interface.test_response = response
92
+ @interface.request_preapproval { |r| }.should == response
93
+ end
94
+
95
+ it "should use passed block to build request" do
96
+ called = false
97
+ @interface.request_preapproval do |p|
98
+ called = true
99
+ p.should be_a(AdaptivePay::PreapprovalRequest)
100
+ end
101
+ called.should be_true
102
+ end
103
+
104
+ end
105
+
106
+ describe "request_payment" do
107
+
108
+ before :each do
109
+ @interface.retain_requests_for_test = true
110
+ end
111
+
112
+ it "should enqueue request if retain_requests_for_test is set" do
113
+ request = nil
114
+ @interface.request_payment do |r|
115
+ request = r
116
+ end
117
+ AdaptivePay::Interface.requests.size.should == 1
118
+ AdaptivePay::Interface.requests.first.should == request
119
+ end
120
+
121
+ it "should allow providing pre canned responses" do
122
+ response = mock(:response)
123
+ AdaptivePay::Interface.test_response = response
124
+ @interface.request_payment { |r| }.should == response
125
+ end
126
+
127
+ it "should use passed block to build request" do
128
+ called = false
129
+ @interface.request_payment do |p|
130
+ called = true
131
+ p.should be_a(AdaptivePay::PaymentRequest)
132
+ end
133
+ called.should be_true
134
+ end
135
+
136
+ end
137
+
138
+ describe "request_refund" do
139
+
140
+ before :each do
141
+ @interface.retain_requests_for_test = true
142
+ end
143
+
144
+ it "should enqueue request if retain_requests_for_test is set" do
145
+ request = nil
146
+ @interface.request_refund do |r|
147
+ request = r
148
+ end
149
+ AdaptivePay::Interface.requests.size.should == 1
150
+ AdaptivePay::Interface.requests.first.should == request
151
+ end
152
+
153
+ it "should allow providing pre canned responses" do
154
+ response = mock(:response)
155
+ AdaptivePay::Interface.test_response = response
156
+ @interface.request_refund { |r| }.should == response
157
+ end
158
+
159
+ it "should use passed block to build request" do
160
+ called = false
161
+ @interface.request_refund do |p|
162
+ called = true
163
+ p.should be_a(AdaptivePay::RefundRequest)
164
+ end
165
+ called.should be_true
166
+ end
167
+
168
+ end
169
+
170
+ describe "perform" do
171
+
172
+ before :each do
173
+ @interface.retain_requests_for_test = true
174
+ end
175
+
176
+ it "should allow providing pre canned responses" do
177
+ response = mock(:response)
178
+ AdaptivePay::Interface.test_response = response
179
+ @interface.perform(AdaptivePay::PreapprovalRequest.new).should == response
180
+ end
181
+
182
+ end
183
+
184
+ end
@@ -0,0 +1,52 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe AdaptivePay::PaymentRequest do
4
+
5
+ before :each do
6
+ @payment_request = AdaptivePay::PaymentRequest.new
7
+ end
8
+
9
+ describe "serialize" do
10
+
11
+ it "should include recipient details in serialization" do
12
+ request = AdaptivePay::PaymentRequest.new
13
+ request.add_recipient :email => "acc@paypal.com", :amount => 100
14
+ request.add_recipient :email => "primary@paypal.com", :amount => 10, :primary => true
15
+ decompose(request.serialize).should == {
16
+ "receiverList.receiver(0).email" => "acc@paypal.com",
17
+ "receiverList.receiver(0).amount" => "100.00",
18
+ "receiverList.receiver(0).primary" => "false",
19
+ "receiverList.receiver(1).email" => "primary@paypal.com",
20
+ "receiverList.receiver(1).amount" => "10.00",
21
+ "receiverList.receiver(1).primary" => "true",
22
+ "actionType" => "PAY",
23
+ "requestEnvelope.errorLanguage" => "en_US"
24
+ }
25
+ end
26
+
27
+ end
28
+
29
+ describe "add_recipient" do
30
+
31
+ it "should add a Recipient to recipients from Recipient" do
32
+ recipient = AdaptivePay::Recipient.new :email => "recipient@paypal.com",
33
+ :amount => BigDecimal.new("10.00"),
34
+ :primary => false
35
+ @payment_request.add_recipient recipient
36
+ @payment_request.recipients.size.should == 1
37
+ @payment_request.recipients.first.should == recipient
38
+ end
39
+
40
+ it "should add a Recipient to recipients from hash" do
41
+ @payment_request.add_recipient :email => "recipient@paypal.com",
42
+ :amount => BigDecimal.new("10.00"),
43
+ :primary => false
44
+ @payment_request.recipients.size.should == 1
45
+ @payment_request.recipients.first.email.should == "recipient@paypal.com"
46
+ @payment_request.recipients.first.amount.should == BigDecimal.new("10.00")
47
+ @payment_request.recipients.first.primary.should == false
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,9 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe AdaptivePay::PreapprovalRequest do
4
+
5
+ before :each do
6
+ @request = AdaptivePay::ApprovalRequest.new
7
+ end
8
+
9
+ end
@@ -0,0 +1,20 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe AdaptivePay::Recipient do
4
+
5
+ before :each do
6
+ @recipient = AdaptivePay::Recipient.new
7
+ end
8
+
9
+ describe "attributes" do
10
+
11
+ %w{email amount primary}.each do |attribute|
12
+ it "should have #{attribute} attribute" do
13
+ @recipient.send("#{attribute}=", "test")
14
+ @recipient.send(attribute).should == "test"
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,51 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe AdaptivePay::RefundRequest do
4
+
5
+ before :each do
6
+ @refund_request = AdaptivePay::RefundRequest.new
7
+ end
8
+
9
+ describe "serialize" do
10
+
11
+ it "should include recipient details in serialization" do
12
+ request = AdaptivePay::RefundRequest.new
13
+ request.add_recipient :email => "acc@paypal.com", :amount => 100
14
+ request.add_recipient :email => "primary@paypal.com", :amount => 10, :primary => true
15
+ decompose(request.serialize).should == {
16
+ "receiverList.receiver(0).email" => "acc@paypal.com",
17
+ "receiverList.receiver(0).amount" => "100.00",
18
+ "receiverList.receiver(0).primary" => "false",
19
+ "receiverList.receiver(1).email" => "primary@paypal.com",
20
+ "receiverList.receiver(1).amount" => "10.00",
21
+ "receiverList.receiver(1).primary" => "true",
22
+ "requestEnvelope.errorLanguage" => "en_US"
23
+ }
24
+ end
25
+
26
+ end
27
+
28
+ describe "add_recipient" do
29
+
30
+ it "should add a Recipient to recipients from Recipient" do
31
+ recipient = AdaptivePay::Recipient.new :email => "recipient@paypal.com",
32
+ :amount => BigDecimal.new("10.00"),
33
+ :primary => false
34
+ @refund_request.add_recipient recipient
35
+ @refund_request.recipients.size.should == 1
36
+ @refund_request.recipients.first.should == recipient
37
+ end
38
+
39
+ it "should add a Recipient to recipients from hash" do
40
+ @refund_request.add_recipient :email => "recipient@paypal.com",
41
+ :amount => BigDecimal.new("10.00"),
42
+ :primary => false
43
+ @refund_request.recipients.size.should == 1
44
+ @refund_request.recipients.first.email.should == "recipient@paypal.com"
45
+ @refund_request.recipients.first.amount.should == BigDecimal.new("10.00")
46
+ @refund_request.recipients.first.primary.should == false
47
+ end
48
+
49
+ end
50
+
51
+ end