merchant_sidekick 0.4.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.
Files changed (63) hide show
  1. data/.gitignore +12 -0
  2. data/Changelog.md +38 -0
  3. data/Gemfile +2 -0
  4. data/MIT-LICENSE +19 -0
  5. data/README.md +88 -0
  6. data/Rakefile +10 -0
  7. data/lib/merchant_sidekick.rb +45 -0
  8. data/lib/merchant_sidekick/active_merchant/credit_card_payment.rb +117 -0
  9. data/lib/merchant_sidekick/active_merchant/gateways/authorize_net_gateway.rb +26 -0
  10. data/lib/merchant_sidekick/active_merchant/gateways/base.rb +29 -0
  11. data/lib/merchant_sidekick/active_merchant/gateways/bogus_gateway.rb +19 -0
  12. data/lib/merchant_sidekick/active_merchant/gateways/paypal_gateway.rb +43 -0
  13. data/lib/merchant_sidekick/addressable/address.rb +400 -0
  14. data/lib/merchant_sidekick/addressable/addressable.rb +353 -0
  15. data/lib/merchant_sidekick/buyer.rb +99 -0
  16. data/lib/merchant_sidekick/gateway.rb +81 -0
  17. data/lib/merchant_sidekick/install.rb +19 -0
  18. data/lib/merchant_sidekick/invoice.rb +179 -0
  19. data/lib/merchant_sidekick/line_item.rb +128 -0
  20. data/lib/merchant_sidekick/migrations/addressable.rb +47 -0
  21. data/lib/merchant_sidekick/migrations/billing.rb +100 -0
  22. data/lib/merchant_sidekick/migrations/shopping_cart.rb +28 -0
  23. data/lib/merchant_sidekick/money.rb +38 -0
  24. data/lib/merchant_sidekick/order.rb +244 -0
  25. data/lib/merchant_sidekick/payment.rb +59 -0
  26. data/lib/merchant_sidekick/purchase_invoice.rb +180 -0
  27. data/lib/merchant_sidekick/purchase_order.rb +350 -0
  28. data/lib/merchant_sidekick/railtie.rb +7 -0
  29. data/lib/merchant_sidekick/sales_invoice.rb +56 -0
  30. data/lib/merchant_sidekick/sales_order.rb +122 -0
  31. data/lib/merchant_sidekick/sellable.rb +88 -0
  32. data/lib/merchant_sidekick/seller.rb +93 -0
  33. data/lib/merchant_sidekick/shopping_cart/cart.rb +225 -0
  34. data/lib/merchant_sidekick/shopping_cart/line_item.rb +152 -0
  35. data/lib/merchant_sidekick/version.rb +3 -0
  36. data/merchant_sidekick.gemspec +37 -0
  37. data/spec/address_spec.rb +153 -0
  38. data/spec/addressable_spec.rb +250 -0
  39. data/spec/buyer_spec.rb +203 -0
  40. data/spec/cart_line_item_spec.rb +58 -0
  41. data/spec/cart_spec.rb +213 -0
  42. data/spec/config/merchant_sidekick.yml +10 -0
  43. data/spec/credit_card_payment_spec.rb +175 -0
  44. data/spec/fixtures/addresses.yml +97 -0
  45. data/spec/fixtures/line_items.yml +18 -0
  46. data/spec/fixtures/orders.yml +24 -0
  47. data/spec/fixtures/payments.yml +17 -0
  48. data/spec/fixtures/products.yml +12 -0
  49. data/spec/fixtures/users.yml +11 -0
  50. data/spec/gateway_spec.rb +136 -0
  51. data/spec/invoice_spec.rb +79 -0
  52. data/spec/line_item_spec.rb +65 -0
  53. data/spec/order_spec.rb +85 -0
  54. data/spec/payment_spec.rb +14 -0
  55. data/spec/purchase_invoice_spec.rb +70 -0
  56. data/spec/purchase_order_spec.rb +191 -0
  57. data/spec/sales_invoice_spec.rb +58 -0
  58. data/spec/sales_order_spec.rb +107 -0
  59. data/spec/schema.rb +28 -0
  60. data/spec/sellable_spec.rb +34 -0
  61. data/spec/seller_spec.rb +201 -0
  62. data/spec/spec_helper.rb +255 -0
  63. metadata +201 -0
@@ -0,0 +1,59 @@
1
+ # Superclass for all payment transaction. Each purchase, authorization, etc. attempt
2
+ # will result in a new sublcass payment instance
3
+ module MerchantSidekick
4
+ class Payment < ActiveRecord::Base
5
+ self.table_name = "payments"
6
+
7
+ #--- associations
8
+ belongs_to :payable, :polymorphic => true
9
+ acts_as_list :scope => 'payable_id=#{quote_value(payable_id)} AND payable_type=#{quote_value(payable_type)}'
10
+
11
+ #--- mixins
12
+ money :amount, :cents => :cents, :currency => :currency
13
+
14
+ #--- class methods
15
+
16
+ # Determines which payment class to use based on the payment object passed.
17
+ # overriden this if other payment types must be supported, e.g. for bank
18
+ # transfer, etc.
19
+ #
20
+ # E.g.
21
+ #
22
+ # Payment.class_for(ActiveMerchant::Billing::CreditCard.new(...))
23
+ # #=> MerchantSidekick::ActiveMerchant::CreditCardPayment
24
+
25
+ def self.class_for(payment_object)
26
+ MerchantSidekick::ActiveMerchant::CreditCardPayment
27
+ end
28
+
29
+ def self.content_column_names
30
+ content_columns.map(&:name) - %w(payable_type payable_id kind reference message action params test cents currency lock_version position type uuid created_at updated_at success)
31
+ end
32
+
33
+ #--- instance methods
34
+
35
+ # override in sublcass
36
+ # infers payment
37
+ def payment_type
38
+ :payment
39
+ end
40
+
41
+ # returns true if the payment transaction was successful
42
+ def success?
43
+ !!(self[:success] || false)
44
+ end
45
+
46
+ # return only attributes with relevant content
47
+ def content_attributes
48
+ self.attributes.reject {|k,v| !self.content_column_names.include?(k.to_s)}.symbolize_keys
49
+ end
50
+
51
+ # returns content column name strings
52
+ def content_column_names
53
+ self.class.content_column_names
54
+ end
55
+ end
56
+
57
+ # MerchantSidekick::AuthorizationError
58
+ class AuthorizationError < StandardError; end
59
+ end
@@ -0,0 +1,180 @@
1
+ # Implements an outbound invoice.
2
+ module MerchantSidekick
3
+ class PurchaseInvoice < Invoice
4
+ belongs_to :purchase_order, :foreign_key => :order_id, :class_name => "MerchantSidekick::PurchaseOrder"
5
+
6
+ # overrides superclass
7
+ def purchase_invoice?
8
+ true
9
+ end
10
+
11
+ # authorizes the payment
12
+ # payment_object is the credit_card instance or other payment objects
13
+ def authorize(payment_object, options={})
14
+ transaction do
15
+ authorization = MerchantSidekick::Payment.class_for(payment_object).authorize(
16
+ gross_total,
17
+ payment_object,
18
+ payment_options(options)
19
+ )
20
+
21
+ self.push_payment(authorization)
22
+
23
+ if authorization.success?
24
+ save(:validate => false)
25
+ payment_authorized!
26
+ else
27
+ # we don't want to save the payment and related objects
28
+ # when the authorization fails
29
+ # transaction_declined!
30
+ end
31
+ authorization
32
+ end
33
+ end
34
+
35
+ # Captures a previously authorized payment. If the gross amount of the
36
+ # invoice has changed between authorization and capture, the difference
37
+ # in case authorization amount - capture amount > 0 will be refunded to
38
+ # the respective account, otherwise an exception thrown.
39
+ # Only payments can be captured, not deposits.
40
+ #
41
+ # E.g.
42
+ #
43
+ # @order = person.purchase(@product)
44
+ # @payment = @order.authorize(@credit_card)
45
+ # ...
46
+ # @order.capture
47
+ # @order.invoice.paid? #=> true
48
+ #
49
+ def capture(options={})
50
+ transaction do
51
+ if authorization
52
+ capture_result = authorization.class.capture(
53
+ gross_total,
54
+ authorization_reference,
55
+ payment_options(authorization.content_attributes.merge(options))
56
+ )
57
+
58
+ self.push_payment(capture_result)
59
+
60
+ save(:validate => false)
61
+
62
+ if capture_result.success?
63
+ payment_captured!
64
+ else
65
+ transaction_declined!
66
+ end
67
+ capture_result
68
+ end
69
+ end
70
+ end
71
+
72
+ # overrides accessor and caches authorization
73
+ def authorization
74
+ @authorization ||= if auth = self.payments.find_by_action_and_success('authorization', true, :order => 'id ASC')
75
+ auth
76
+ elsif auth = self.payments.find_by_action_and_success('purchase', true, :order => 'id ASC')
77
+ auth
78
+ end
79
+ end
80
+
81
+ def authorization_reference
82
+ authorization.reference if authorization
83
+ end
84
+
85
+ # void a previously authorized payment
86
+ def void(options={})
87
+ transaction do
88
+ if authorization
89
+ void_result = authorization.class.void(
90
+ gross_total,
91
+ authorization_reference,
92
+ payment_options(authorization.content_attributes.merge(options))
93
+ )
94
+ self.push_payment(void_result)
95
+
96
+ save(:validate => false)
97
+
98
+ if void_result.success?
99
+ payment_voided!
100
+ else
101
+ transaction_declined!
102
+ end
103
+ void_result
104
+ end
105
+ end
106
+ end
107
+
108
+ # refunds the entire amount or the amount provided
109
+ # of the invoice
110
+ #
111
+ # Options:
112
+ # :card_number must be supplied in the options
113
+ # :amount => specify the amount to be refunded
114
+ #
115
+ def credit(options={})
116
+ transaction do
117
+ if authorization
118
+ credit_result = authorization.class.credit(
119
+ options[:amount] || gross_total,
120
+ authorization_reference,
121
+ payment_options(authorization.content_attributes.merge(options))
122
+ )
123
+ self.push_payment(credit_result)
124
+
125
+ save(:validate => false)
126
+
127
+ if credit_result.success?
128
+ payment_refunded!
129
+ else
130
+ transaction_declined!
131
+ end
132
+ capture_result
133
+ end
134
+ end
135
+ end
136
+
137
+ # Purchase invoice, combines authorization and capture in one step
138
+ def purchase(payment_object, options={})
139
+ transaction do
140
+ purchase_result = MerchantSidekick::Payment.class_for(payment_object).purchase(
141
+ gross_total,
142
+ payment_object,
143
+ payment_options(options)
144
+ )
145
+ self.push_payment(purchase_result)
146
+
147
+ save(:validate => false)
148
+
149
+ if purchase_result.success?
150
+ payment_paid!
151
+ else
152
+ transaction_declined!
153
+ end
154
+ purchase_result
155
+ end
156
+ end
157
+
158
+ # returns a hash of additional merchant data passed to authorize
159
+ # you want to pass in the following additional options
160
+ #
161
+ # :ip => ip address of the buyer
162
+ #
163
+ def payment_options(options={})
164
+ { # general
165
+ :buyer => self.buyer,
166
+ :seller => self.seller,
167
+ :payable => self,
168
+ # active merchant relevant
169
+ :customer => self.buyer ? "#{self.buyer.class.name} (#{self.buyer.id})" : nil,
170
+ :merchant => self.seller ? "#{self.seller.class.name} (#{self.seller.id})" : nil,
171
+ :email => self.buyer && self.buyer.respond_to?(:email) ? self.buyer.email : nil,
172
+ :invoice => self.number,
173
+ :currency => self.currency,
174
+ :billing_address => self.billing_address ? self.billing_address.to_merchant_attributes : nil,
175
+ :shipping_address => self.shipping_address ? self.shipping_address.to_merchant_attributes : nil
176
+ }.merge(options)
177
+ end
178
+
179
+ end
180
+ end
@@ -0,0 +1,350 @@
1
+ # Implements outbound orders, i.e. the merchant sells a product to a user
2
+ module MerchantSidekick
3
+ class PurchaseOrder < Order
4
+ has_many :purchase_invoices, :foreign_key => :order_id, :class_name => "MerchantSidekick::PurchaseInvoice"
5
+
6
+ # Authorizes a payment over the order gross amount
7
+ def authorize(payment_object, options={})
8
+ defaults = {:order_id => number}
9
+ options = defaults.merge(options).symbolize_keys
10
+ transaction do
11
+ buyer.send(:before_authorize_payment, self) if buyer && buyer.respond_to?(:before_authorize_payment)
12
+ self.build_addresses
13
+ self.build_invoice unless self.last_unsaved_invoice
14
+ authorization_result = self.last_unsaved_invoice.authorize(payment_object, options)
15
+ if authorization_result.success?
16
+ process_payment!
17
+ end
18
+ buyer.send(:after_authorize_payment, self) if buyer && buyer.respond_to?(:after_authorize_payment)
19
+ authorization_result
20
+ end
21
+ end
22
+
23
+ # Captures the amount of the order that was previously authorized
24
+ # If the capture amount
25
+ def capture(options={})
26
+ defaults = {:order_id => number}
27
+ options = defaults.merge(options).symbolize_keys
28
+
29
+ if invoice = self.purchase_invoices.find(:all, :created_at => "invoices.id ASC").last
30
+ buyer.send(:before_capture_payment, self) if buyer && buyer.respond_to?(:before_capture_payment)
31
+ capture_result = invoice.capture(options)
32
+ if capture_result.success?
33
+ approve_payment!
34
+ end
35
+ buyer.send(:after_capture_payment, self) if buyer && buyer.respond_to?(:after_capture_payment)
36
+ capture_result
37
+ end
38
+ end
39
+
40
+ # Pay the order and generate invoice
41
+ def pay(payment_object, options={})
42
+ defaults = { :order_id => number }
43
+ options = defaults.merge(options).symbolize_keys
44
+
45
+ # fire buyer's before_payment callback
46
+ buyer.send(:before_payment, self) if buyer && buyer.respond_to?(:before_payment)
47
+ self.build_addresses
48
+ self.build_invoice unless self.last_unsaved_invoice
49
+ payment = self.last_unsaved_invoice.purchase(payment_object, options)
50
+ if payment.success?
51
+ process_payment!
52
+ approve_payment!
53
+ end
54
+ save!
55
+ # fire buyer's after_payment callback
56
+ buyer.send(:after_payment, self ) if buyer && buyer.respond_to?(:after_payment)
57
+ payment
58
+ end
59
+
60
+ # Voids a previously authorized invoice payment and sets the status to cancel
61
+ # Usage:
62
+ # void(options = {})
63
+ #
64
+ def void(options={})
65
+ defaults = { :order_id => self[:number] }
66
+ options = defaults.merge(options).symbolize_keys
67
+
68
+ if invoice = self.purchase_invoices.find(:all, :created_at => "invoices.id ASC").last
69
+ # before_payment
70
+ buyer.send(:before_void_payment, self ) if buyer && buyer.respond_to?(:before_void_payment)
71
+ voided_result = invoice.void(options)
72
+ if voided_result.success?
73
+ cancel!
74
+ end
75
+ save!
76
+ # after_void_payment
77
+ buyer.send(:after_void_payment, self) if buyer && buyer.respond_to?(:after_void_payment)
78
+ voided_result
79
+ end
80
+ end
81
+
82
+ # refunds a previously paid order
83
+ # Note: :card_number must be supplied
84
+ def refund(options={})
85
+ defaults = { :order_id => number }
86
+ options = defaults.merge(options).symbolize_keys
87
+
88
+ if (invoice = self.purchase_invoices.find(:all, :created_at => "invoices.id ASC").last) && invoice.paid?
89
+ # fire buyer's before_refund_payment callback
90
+ buyer.send(:before_refund_payment, self) if buyer && buyer.respond_to?(:before_refund_payment)
91
+ refunded_result = invoice.credit(options)
92
+ if refunded_result.success?
93
+ refund!
94
+ end
95
+ save!
96
+ # fire buyer's after_refund_payment callback
97
+ buyer.send(:after_refund_payment, self) if buyer && buyer.respond_to?(:after_refund_payment)
98
+ refunded_result
99
+ end
100
+ end
101
+
102
+ # E.g.
103
+ #
104
+ # @order.recurring(@payment, :interval => {:length => 1, :unit => :month},
105
+ # :duration => {:start_date => Date.today, :occurrences => 999})
106
+ #
107
+ def recurring(payment_object, options={})
108
+ defaults = {:order_id => number}
109
+ options = defaults.merge(options).symbolize_keys
110
+
111
+ self.build_addresses
112
+
113
+ authorization = Payment.class_for(payment_object).recurring(
114
+ gross_total, payment_object, payment_options(options))
115
+ self.push_payment(authorization)
116
+ if authorization.success?
117
+ save(false)
118
+ process_payment!
119
+ else
120
+ # we don't want to save the payment and related objects
121
+ # when the authorization fails
122
+ # transaction_declined!
123
+ end
124
+ authorization
125
+ end
126
+
127
+ # E.g.
128
+ #
129
+ # @order.pay_recurring("c3s34", :add_line_items => @line_items)
130
+ #
131
+ def pay_recurring(authorization=nil, options={})
132
+ # recurring payment
133
+ recurring_payment = if authorization.nil?
134
+ self.payments.recurring.find(:all, :order => "payments.id ASC").last
135
+ elsif authorization.is_a?(Payment) && self.payments.include?(authorization)
136
+ authorization
137
+ else
138
+ self.payments.find(:first, :conditions => ["payments.id = ? OR payments.reference = ? OR payments.uuid = ?", authorization])
139
+ end
140
+
141
+ # recurring expired
142
+ if !self.pending? || self.purchase_invoices.paid.count > recurring_payment.duration_occurrences
143
+ raise MerchantSidekick::RecurringPaymentError, "Recurring order #{self.number} expired"
144
+ end
145
+
146
+ transaction do
147
+ buyer.send(:before_pay_recurring, self) if buyer && buyer.respond_to?(:before_pay_recurring)
148
+ self.additional_line_items(options[:add_line_items])
149
+ self.build_invoice unless self.last_unsaved_invoice
150
+
151
+ authorization_result = self.last_unsaved_invoice.purchase(recurring_payment, options)
152
+ if authorization_result.success?
153
+ # add next billing due date
154
+ process_payment!
155
+ end
156
+ buyer.send(:after_pay_recurring, self) if buyer && buyer.respond_to?(:before_pay_recurring)
157
+ authorization_result
158
+ end
159
+ end
160
+
161
+ def authorize_recurring(authorization=nil, options={})
162
+ # recurring payment
163
+ recurring_payment = if authorization.nil?
164
+ self.payments.recurring.find(:all, :order => "payments.id ASC").last
165
+ elsif authorization.is_a?(Payment) && self.payments.include?(authorization)
166
+ authorization
167
+ else
168
+ self.payments.find(:first, :conditions => ["payments.id = ? OR payments.reference = ? OR payments.uuid = ?", authorization])
169
+ end
170
+
171
+ # recurring expired
172
+ if !self.pending? || self.purchase_invoices.paid.count > recurring_payment.duration_occurrences
173
+ raise MerchantSidekick::RecurringPaymentError, "Recurring order #{self.number} expired"
174
+ end
175
+
176
+ transaction do
177
+ buyer.send(:before_authorize_recurring, self) if buyer && buyer.respond_to?(:before_authorize_recurring)
178
+ self.additional_line_items(options[:add_line_items])
179
+ self.build_invoice unless self.last_unsaved_invoice
180
+
181
+ authorization_result = self.last_unsaved_invoice.authorize(recurring_payment, options)
182
+ if authorization_result.success?
183
+ process_payment!
184
+ end
185
+ buyer.send(:after_authorize_recurring, self) if buyer && buyer.respond_to?(:after_authorize_recurring)
186
+ authorization_result
187
+ end
188
+ end
189
+
190
+ # returns a hash of additional merchant data passed to authorize
191
+ # you want to pass in the following additional options
192
+ #
193
+ # :ip => ip address of the buyer
194
+ #
195
+ def payment_options(options={})
196
+ { # general
197
+ :buyer => self.buyer,
198
+ :seller => self.seller,
199
+ :payable => self,
200
+ # active merchant relevant
201
+ :customer => self.buyer ? "#{self.buyer.name} (#{self.buyer.id})" : nil,
202
+ :email => self.buyer && self.buyer.respond_to?(:email) ? self.buyer.email : nil,
203
+ :order_number => self.number,
204
+ #:invoice => self.number,
205
+ :merchant => self.seller ? "#{self.seller.name} (#{self.seller.id})" : nil,
206
+ :currency => self.currency,
207
+ :billing_address => self.billing_address ? self.billing_address.to_merchant_attributes : nil,
208
+ :shipping_address => self.shipping_address ? self.shipping_address.to_merchant_attributes : nil
209
+ }.merge(options)
210
+ end
211
+
212
+ # yes, i am a purchase order!
213
+ def purchase_order?
214
+ true
215
+ end
216
+
217
+ # used in build_invoice to determine which type of invoice
218
+ def to_invoice_class_name
219
+ "MerchantSidekick::PurchaseInvoice"
220
+ end
221
+
222
+ # returns last unsaved invoice
223
+ def last_unsaved_invoice
224
+ unless self.purchase_invoices.empty?
225
+ self.purchase_invoices.last.new_record? ? self.purchase_invoices.last : nil
226
+ else
227
+ nil
228
+ end
229
+ end
230
+
231
+ def build_invoice
232
+ new_invoice = self.purchase_invoices.build(
233
+ :line_items => self.duplicate_line_items,
234
+ :net_amount => self.net_total,
235
+ :tax_amount => self.tax_total,
236
+ :gross_amount => self.gross_total,
237
+ :buyer => self.buyer,
238
+ :seller => self.seller,
239
+ :origin_address => self.origin_address ? self.origin_address.dup : nil,
240
+ :billing_address => self.billing_address ? self.billing_address.dup : nil,
241
+ :shipping_address => self.shipping_address ? self.shipping_address.dup : nil
242
+ )
243
+
244
+ # set new invoice's line items to invoice we just created
245
+ new_invoice.line_items.each do |li|
246
+ if li.new_record?
247
+ li.invoice = new_invoice
248
+ else
249
+ li.update_attribute(:invoice, new_invoice)
250
+ end
251
+ end
252
+
253
+ # duplicate addresses
254
+ new_invoice.build_origin_address(self.origin_address.content_attributes) if self.origin_address
255
+ new_invoice.build_billing_address(self.billing_address.content_attributes) if self.billing_address
256
+ new_invoice.build_shipping_address(self.shipping_address.content_attributes) if self.shipping_address
257
+
258
+ new_invoice.evaluate
259
+ @additional_line_items = nil
260
+ new_invoice
261
+ end
262
+
263
+ # make sure we get a copy of the line items
264
+ def duplicate_line_items
265
+ result = []
266
+ lis = self.line_items
267
+ lis += @additional_line_items.to_a
268
+ lis.each do |line_item|
269
+ li = line_item.dup # Note: use clone in Rails < 3.1
270
+ li.sellable = line_item.sellable
271
+ li.net_amount = line_item.net_amount
272
+ li.gross_amount = line_item.gross_amount
273
+ li.order = nil
274
+ result << li
275
+ end
276
+ result
277
+ end
278
+
279
+ # process additional line items before build invoice
280
+ def additional_line_items(items)
281
+ # add line items
282
+ if !items.blank?
283
+ if items.is_a?(Array) && items.all? {|i| i.is_a?(MerchantSidekick::ShoppingCart::LineItem)}
284
+ duped = self.buyer.dup
285
+ order = duped.purchase items
286
+ @additional_line_items = order.line_items
287
+ elsif items.is_a?(Array) && items.all? {|i| i.is_a?(LineItem)}
288
+ @additional_line_items = items
289
+ elsif items.is_a?(Array) && items.all? {|i| i.is_a?(Product)}
290
+ # array of products
291
+ duped = self.buyer.dup
292
+ cart = Cart.new(self.currency)
293
+ items.each {|product| cart.add(product)}
294
+ order = duped.purchase cart.line_items
295
+ @additional_line_items = order.line_items
296
+ end
297
+ end
298
+ end
299
+
300
+ # Builds billing, shipping and origin addresses
301
+ def build_addresses(options={})
302
+ raise ArgumentError.new("No address declared for buyer (#{buyer.class.name} ##{buyer.id}), use e.g. class #{buyer.class.name}; has_address; end") \
303
+ unless buyer.respond_to?(:find_default_address)
304
+
305
+ # buyer's billing or default address
306
+ unless default_billing_address
307
+ if buyer.respond_to?(:billing_address) && buyer.default_billing_address
308
+ self.build_billing_address(buyer.default_billing_address.content_attributes)
309
+ else
310
+ if buyer_default_address = buyer.find_default_address
311
+ self.build_billing_address(buyer_default_address.content_attributes)
312
+ else
313
+ raise ArgumentError.new(
314
+ "No billing or default address for buyer (#{buyer.class.name} ##{buyer.id}), use e.g. class #{buyer.class.name}; has_address :billing; end")
315
+ end
316
+ end
317
+ end
318
+
319
+ # buyer's shipping address
320
+ if buyer.respond_to?(:shipping_address)
321
+ self.build_shipping_address(buyer.find_shipping_address_or_clone_from(
322
+ self.billing_address
323
+ ).content_attributes) unless self.default_shipping_address
324
+ end
325
+
326
+ self.billing_address.street = "#{Merchant::Sidekick::Version::NAME}" if self.billing_address && self.billing_address.street.to_s =~ /^backend$/
327
+ self.shipping_address.street = "#{Merchant::Sidekick::Version::NAME}" if self.shipping_address && self.shipping_address.street.to_s =~ /^backend$/
328
+
329
+ # seller's billing or default address
330
+ if seller
331
+ raise ArgumentError.new("No address for seller (#{seller.class.name} ##{seller.id}), use acts_as_addressable") \
332
+ unless seller.respond_to?(:find_default_address)
333
+
334
+ unless default_origin_address
335
+ if seller.respond_to?(:billing_address) && seller.find_billing_address
336
+ self.build_origin_address(seller.find_billing_address.content_attributes)
337
+ else
338
+ if seller_default_address = seller.find_default_address
339
+ self.build_origin_address(seller_default_address.content_attributes)
340
+ end
341
+ end
342
+ end
343
+ end
344
+ end
345
+
346
+ end
347
+
348
+ class ::MerchantSidekick::RecurringPaymentError < Exception
349
+ end
350
+ end