express-checkout 0.0.1 → 0.0.2

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,12 @@
1
+ module Express
2
+ class Exception
3
+ class HttpError < Exception
4
+ attr_accessor :code, :message, :body
5
+ def initialize(code, message, body = '')
6
+ @code = code
7
+ @message = message
8
+ @body = body
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,23 @@
1
+ module Express
2
+ module IPN
3
+ def self.endpoint
4
+ _endpoint_ = URI.parse Paypal.endpoint
5
+ _endpoint_.query = {
6
+ cmd: '_notify-validate'
7
+ }.to_query
8
+ _endpoint_.to_s
9
+ end
10
+
11
+ def self.verify!(raw_post)
12
+ response = RestClient.post(
13
+ endpoint, raw_post
14
+ )
15
+ case response.body
16
+ when 'VERIFIED'
17
+ true
18
+ else
19
+ raise Exception::APIError.new(response.body)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,64 @@
1
+ module Express
2
+ module NVP
3
+ class Request < Base
4
+ attr_required :username, :password, :signature
5
+ attr_optional :subject
6
+ attr_accessor :version
7
+
8
+ ENDPOINT = {
9
+ :production => 'https://api-3t.paypal.com/nvp',
10
+ :sandbox => 'https://api-3t.sandbox.paypal.com/nvp'
11
+ }
12
+
13
+ def self.endpoint
14
+ if Paypal.sandbox?
15
+ ENDPOINT[:sandbox]
16
+ else
17
+ ENDPOINT[:production]
18
+ end
19
+ end
20
+
21
+ def initialize(attributes = {})
22
+ @version = Paypal.api_version
23
+ super
24
+ end
25
+
26
+ def common_params
27
+ {
28
+ :USER => self.username,
29
+ :PWD => self.password,
30
+ :SIGNATURE => self.signature,
31
+ :SUBJECT => self.subject,
32
+ :VERSION => self.version
33
+ }
34
+ end
35
+
36
+ def request(method, params = {})
37
+ handle_response do
38
+ post(method, params)
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def post(method, params)
45
+ RestClient.post(self.class.endpoint, common_params.merge(params).merge(:METHOD => method))
46
+ end
47
+
48
+ def handle_response
49
+ response = yield
50
+ response = CGI.parse(response).inject({}) do |res, (k, v)|
51
+ res.merge!(k.to_sym => v.first)
52
+ end
53
+ case response[:ACK]
54
+ when 'Success', 'SuccessWithWarning'
55
+ response
56
+ else
57
+ raise Exception::APIError.new(response)
58
+ end
59
+ rescue RestClient::Exception => e
60
+ raise Exception::HttpError.new(e.http_code, e.message, e.http_body)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,233 @@
1
+ module Express
2
+ module NVP
3
+ class Response < Base
4
+ cattr_reader :attribute_mapping
5
+ @@attribute_mapping = {
6
+ :ACK => :ack,
7
+ :BUILD => :build,
8
+ :BILLINGAGREEMENTACCEPTEDSTATUS => :billing_agreement_accepted_status,
9
+ :CHECKOUTSTATUS => :checkout_status,
10
+ :CORRELATIONID => :correlation_id,
11
+ :COUNTRYCODE => :country_code,
12
+ :CURRENCYCODE => :currency_code,
13
+ :DESC => :description,
14
+ :NOTIFYURL => :notify_url,
15
+ :TIMESTAMP => :timestamp,
16
+ :TOKEN => :token,
17
+ :VERSION => :version,
18
+ # Some of the attributes below are duplicates of what
19
+ # exists in the payment response, but paypal doesn't
20
+ # prefix these with PAYMENTREQUEST when issuing a
21
+ # GetTransactionDetails response.
22
+ :RECEIVEREMAIL => :receiver_email,
23
+ :RECEIVERID => :receiver_id,
24
+ :SUBJECT => :subject,
25
+ :TRANSACTIONID => :transaction_id,
26
+ :TRANSACTIONTYPE => :transaction_type,
27
+ :PAYMENTTYPE => :payment_type,
28
+ :ORDERTIME => :order_time,
29
+ :PAYMENTSTATUS => :payment_status,
30
+ :PENDINGREASON => :pending_reason,
31
+ :REASONCODE => :reason_code,
32
+ :PROTECTIONELIGIBILITY => :protection_eligibility,
33
+ :PROTECTIONELIGIBILITYTYPE => :protection_eligibility_type,
34
+ :ADDRESSOWNER => :address_owner,
35
+ :ADDRESSSTATUS => :address_status,
36
+ :INVNUM => :invoice_number,
37
+ :CUSTOM => :custom
38
+ }
39
+ attr_accessor *@@attribute_mapping.values
40
+ attr_accessor :shipping_options_is_default, :success_page_redirect_requested, :insurance_option_selected
41
+ attr_accessor :amount, :description, :ship_to, :bill_to, :payer, :recurring, :billing_agreement, :refund
42
+ attr_accessor :payment_responses, :payment_info, :items
43
+ alias_method :colleration_id, :correlation_id # NOTE: I made a typo :p
44
+
45
+ def initialize(attributes = {})
46
+ attrs = attributes.dup
47
+ @@attribute_mapping.each do |key, value|
48
+ self.send "#{value}=", attrs.delete(key)
49
+ end
50
+ @shipping_options_is_default = attrs.delete(:SHIPPINGOPTIONISDEFAULT) == 'true'
51
+ @success_page_redirect_requested = attrs.delete(:SUCCESSPAGEREDIRECTREQUESTED) == 'true'
52
+ @insurance_option_selected = attrs.delete(:INSURANCEOPTIONSELECTED) == 'true'
53
+ @amount = Payment::Common::Amount.new(
54
+ :total => attrs.delete(:AMT),
55
+ :item => attrs.delete(:ITEMAMT),
56
+ :handing => attrs.delete(:HANDLINGAMT),
57
+ :insurance => attrs.delete(:INSURANCEAMT),
58
+ :ship_disc => attrs.delete(:SHIPDISCAMT),
59
+ :shipping => attrs.delete(:SHIPPINGAMT),
60
+ :tax => attrs.delete(:TAXAMT),
61
+ :fee => attrs.delete(:FEEAMT)
62
+ )
63
+ @ship_to = Payment::Response::Address.new(
64
+ :owner => attrs.delete(:SHIPADDRESSOWNER),
65
+ :status => attrs.delete(:SHIPADDRESSSTATUS),
66
+ :name => attrs.delete(:SHIPTONAME),
67
+ :zip => attrs.delete(:SHIPTOZIP),
68
+ :street => attrs.delete(:SHIPTOSTREET),
69
+ :street2 => attrs.delete(:SHIPTOSTREET2),
70
+ :city => attrs.delete(:SHIPTOCITY),
71
+ :state => attrs.delete(:SHIPTOSTATE),
72
+ :country_code => attrs.delete(:SHIPTOCOUNTRYCODE),
73
+ :country_name => attrs.delete(:SHIPTOCOUNTRYNAME)
74
+ )
75
+ @bill_to = Payment::Response::Address.new(
76
+ :owner => attrs.delete(:ADDRESSID),
77
+ :status => attrs.delete(:ADDRESSSTATUS),
78
+ :name => attrs.delete(:BILLINGNAME),
79
+ :zip => attrs.delete(:ZIP),
80
+ :street => attrs.delete(:STREET),
81
+ :street2 => attrs.delete(:STREET2),
82
+ :city => attrs.delete(:CITY),
83
+ :state => attrs.delete(:STATE),
84
+ :country_code => attrs.delete(:COUNTRY)
85
+ )
86
+ if attrs[:PAYERID]
87
+ @payer = Payment::Response::Payer.new(
88
+ :identifier => attrs.delete(:PAYERID),
89
+ :status => attrs.delete(:PAYERSTATUS),
90
+ :first_name => attrs.delete(:FIRSTNAME),
91
+ :last_name => attrs.delete(:LASTNAME),
92
+ :email => attrs.delete(:EMAIL),
93
+ :company => attrs.delete(:BUSINESS)
94
+ )
95
+ end
96
+ if attrs[:PROFILEID]
97
+ @recurring = Payment::Recurring.new(
98
+ :identifier => attrs.delete(:PROFILEID),
99
+ # NOTE:
100
+ # CreateRecurringPaymentsProfile returns PROFILESTATUS
101
+ # GetRecurringPaymentsProfileDetails returns STATUS
102
+ :description => @description,
103
+ :status => attrs.delete(:STATUS) || attrs.delete(:PROFILESTATUS),
104
+ :start_date => attrs.delete(:PROFILESTARTDATE),
105
+ :name => attrs.delete(:SUBSCRIBERNAME),
106
+ :reference => attrs.delete(:PROFILEREFERENCE),
107
+ :auto_bill => attrs.delete(:AUTOBILLOUTAMT),
108
+ :max_fails => attrs.delete(:MAXFAILEDPAYMENTS),
109
+ :aggregate_amount => attrs.delete(:AGGREGATEAMT),
110
+ :aggregate_optional_amount => attrs.delete(:AGGREGATEOPTIONALAMT),
111
+ :final_payment_date => attrs.delete(:FINALPAYMENTDUEDATE)
112
+ )
113
+ if attrs[:BILLINGPERIOD]
114
+ @recurring.billing = Payment::Recurring::Billing.new(
115
+ :amount => @amount,
116
+ :currency_code => @currency_code,
117
+ :period => attrs.delete(:BILLINGPERIOD),
118
+ :frequency => attrs.delete(:BILLINGFREQUENCY),
119
+ :total_cycles => attrs.delete(:TOTALBILLINGCYCLES),
120
+ :trial => {
121
+ :period => attrs.delete(:TRIALBILLINGPERIOD),
122
+ :frequency => attrs.delete(:TRIALBILLINGFREQUENCY),
123
+ :total_cycles => attrs.delete(:TRIALTOTALBILLINGCYCLES),
124
+ :currency_code => attrs.delete(:TRIALCURRENCYCODE),
125
+ :amount => attrs.delete(:TRIALAMT),
126
+ :tax_amount => attrs.delete(:TRIALTAXAMT),
127
+ :shipping_amount => attrs.delete(:TRIALSHIPPINGAMT),
128
+ :paid => attrs.delete(:TRIALAMTPAID)
129
+ }
130
+ )
131
+ end
132
+ if attrs[:REGULARAMT]
133
+ @recurring.regular_billing = Payment::Recurring::Billing.new(
134
+ :amount => attrs.delete(:REGULARAMT),
135
+ :shipping_amount => attrs.delete(:REGULARSHIPPINGAMT),
136
+ :tax_amount => attrs.delete(:REGULARTAXAMT),
137
+ :currency_code => attrs.delete(:REGULARCURRENCYCODE),
138
+ :period => attrs.delete(:REGULARBILLINGPERIOD),
139
+ :frequency => attrs.delete(:REGULARBILLINGFREQUENCY),
140
+ :total_cycles => attrs.delete(:REGULARTOTALBILLINGCYCLES),
141
+ :paid => attrs.delete(:REGULARAMTPAID)
142
+ )
143
+ @recurring.summary = Payment::Recurring::Summary.new(
144
+ :next_billing_date => attrs.delete(:NEXTBILLINGDATE),
145
+ :cycles_completed => attrs.delete(:NUMCYCLESCOMPLETED),
146
+ :cycles_remaining => attrs.delete(:NUMCYCLESREMAINING),
147
+ :outstanding_balance => attrs.delete(:OUTSTANDINGBALANCE),
148
+ :failed_count => attrs.delete(:FAILEDPAYMENTCOUNT),
149
+ :last_payment_date => attrs.delete(:LASTPAYMENTDATE),
150
+ :last_payment_amount => attrs.delete(:LASTPAYMENTAMT)
151
+ )
152
+ end
153
+ end
154
+ if attrs[:BILLINGAGREEMENTID]
155
+ @billing_agreement = Payment::Response::Reference.new(
156
+ :identifier => attrs.delete(:BILLINGAGREEMENTID),
157
+ :description => attrs.delete(:BILLINGAGREEMENTDESCRIPTION),
158
+ :status => attrs.delete(:BILLINGAGREEMENTSTATUS)
159
+ )
160
+ billing_agreement_info = Payment::Response::Info.attribute_mapping.keys.inject({}) do |billing_agreement_info, key|
161
+ billing_agreement_info.merge! key => attrs.delete(key)
162
+ end
163
+ @billing_agreement.info = Payment::Response::Info.new billing_agreement_info
164
+ @billing_agreement.info.amount = @amount
165
+ end
166
+ if attrs[:REFUNDTRANSACTIONID]
167
+ @refund = Payment::Response::Refund.new(
168
+ :transaction_id => attrs.delete(:REFUNDTRANSACTIONID),
169
+ :amount => {
170
+ :total => attrs.delete(:TOTALREFUNDEDAMOUNT),
171
+ :fee => attrs.delete(:FEEREFUNDAMT),
172
+ :gross => attrs.delete(:GROSSREFUNDAMT),
173
+ :net => attrs.delete(:NETREFUNDAMT)
174
+ }
175
+ )
176
+ end
177
+
178
+ # payment_responses
179
+ payment_responses = []
180
+ attrs.keys.each do |_attr_|
181
+ prefix, index, key = case _attr_.to_s
182
+ when /^PAYMENTREQUEST/, /^PAYMENTREQUESTINFO/
183
+ _attr_.to_s.split('_')
184
+ when /^L_PAYMENTREQUEST/
185
+ _attr_.to_s.split('_')[1..-1]
186
+ end
187
+ if prefix
188
+ payment_responses[index.to_i] ||= {}
189
+ payment_responses[index.to_i][key.to_sym] = attrs.delete(_attr_)
190
+ end
191
+ end
192
+ @payment_responses = payment_responses.collect do |_attrs_|
193
+ Payment::Response.new _attrs_
194
+ end
195
+
196
+ # payment_info
197
+ payment_info = []
198
+ attrs.keys.each do |_attr_|
199
+ prefix, index, key = _attr_.to_s.split('_')
200
+ if prefix == 'PAYMENTINFO'
201
+ payment_info[index.to_i] ||= {}
202
+ payment_info[index.to_i][key.to_sym] = attrs.delete(_attr_)
203
+ end
204
+ end
205
+ @payment_info = payment_info.collect do |_attrs_|
206
+ Payment::Response::Info.new _attrs_
207
+ end
208
+
209
+ # payment_info
210
+ items = []
211
+ attrs.keys.each do |_attr_|
212
+ key, index = _attr_.to_s.scan(/^L_(.+?)(\d+)$/).flatten
213
+ if index
214
+ items[index.to_i] ||= {}
215
+ items[index.to_i][key.to_sym] = attrs.delete(_attr_)
216
+ end
217
+ end
218
+ @items = items.collect do |_attrs_|
219
+ Payment::Response::Item.new _attrs_
220
+ end
221
+
222
+ # remove duplicated parameters
223
+ attrs.delete(:SHIPTOCOUNTRY) # NOTE: Same with SHIPTOCOUNTRYCODE
224
+ attrs.delete(:SALESTAX) # Same as TAXAMT
225
+
226
+ # warn ignored attrs
227
+ attrs.each do |key, value|
228
+ Paypal.log "Ignored Parameter (#{self.class}): #{key}=#{value}", :warn
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,13 @@
1
+ module Express
2
+ module Payment
3
+ module Common
4
+ class Amount < Base
5
+ attr_optional :total, :item, :fee, :handing, :insurance, :ship_disc, :shipping, :tax, :gross, :net
6
+
7
+ def numeric_attribute?(key)
8
+ true
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,43 @@
1
+ module Express
2
+ module Payment
3
+ class Recurring < Base
4
+ attr_optional :start_date, :description, :identifier, :status, :name, :reference, :max_fails, :auto_bill, :aggregate_amount, :aggregate_optional_amount, :final_payment_date
5
+ attr_accessor :activation, :billing, :regular_billing, :summary
6
+
7
+ def initialize(attributes = {})
8
+ super
9
+ @activation = Activation.new attributes[:activation] if attributes[:activation]
10
+ @billing = Billing.new attributes[:billing] if attributes[:billing]
11
+ @regular_billing = Billing.new attributes[:regular_billing] if attributes[:regular_billing]
12
+ @summary = Summary.new attributes[:summary] if attributes[:summary]
13
+ end
14
+
15
+ def to_params
16
+ params = [
17
+ self.billing,
18
+ self.activation
19
+ ].compact.inject({}) do |params, attribute|
20
+ params.merge! attribute.to_params
21
+ end
22
+ if self.start_date.is_a?(Time)
23
+ self.start_date = self.start_date.to_s(:db)
24
+ end
25
+ params.merge!(
26
+ :DESC => self.description,
27
+ :MAXFAILEDPAYMENTS => self.max_fails,
28
+ :AUTOBILLOUTAMT => self.auto_bill,
29
+ :PROFILESTARTDATE => self.start_date,
30
+ :SUBSCRIBERNAME => self.name,
31
+ :PROFILEREFERENCE => self.reference
32
+ )
33
+ params.delete_if do |k, v|
34
+ v.blank?
35
+ end
36
+ end
37
+
38
+ def numeric_attribute?(key)
39
+ super || [:max_fails, :failed_count].include?(key)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,14 @@
1
+ module Express
2
+ module Payment
3
+ class Recurring::Activation < Base
4
+ attr_optional :initial_amount, :failed_action
5
+
6
+ def to_params
7
+ {
8
+ :INITAMT => Util.formatted_amount(self.initial_amount),
9
+ :FAILEDINITAMTACTION => self.failed_action
10
+ }
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,39 @@
1
+ module Express
2
+ module Payment
3
+ class Recurring::Billing < Base
4
+ attr_optional :period, :frequency, :paid, :currency_code, :total_cycles
5
+ attr_accessor :amount, :trial
6
+
7
+ def initialize(attributes = {})
8
+ @amount = if attributes[:amount].is_a?(Common::Amount)
9
+ attributes[:amount]
10
+ else
11
+ Common::Amount.new(
12
+ :total => attributes[:amount],
13
+ :tax => attributes[:tax_amount],
14
+ :shipping => attributes[:shipping_amount]
15
+ )
16
+ end
17
+ @trial = Recurring::Billing.new(attributes[:trial]) if attributes[:trial].present?
18
+ super
19
+ end
20
+
21
+ def to_params
22
+ trial_params = (trial.try(:to_params) || {}).inject({}) do |trial_params, (key, value)|
23
+ trial_params.merge(
24
+ :"TRIAL#{key}" => value
25
+ )
26
+ end
27
+ trial_params.merge(
28
+ :BILLINGPERIOD => self.period,
29
+ :BILLINGFREQUENCY => self.frequency,
30
+ :TOTALBILLINGCYCLES => self.total_cycles,
31
+ :AMT => Util.formatted_amount(self.amount.total),
32
+ :CURRENCYCODE => self.currency_code,
33
+ :SHIPPINGAMT => Util.formatted_amount(self.amount.shipping),
34
+ :TAXAMT => Util.formatted_amount(self.amount.tax)
35
+ )
36
+ end
37
+ end
38
+ end
39
+ end