diff --git a/lib/recurly.rb b/lib/recurly.rb index 90d601888..deac0feab 100644 --- a/lib/recurly.rb +++ b/lib/recurly.rb @@ -4,6 +4,7 @@ module Recurly require 'recurly/helper' require 'recurly/api' require 'recurly/resource' + require 'recurly/shipping_address' require 'recurly/billing_info' require 'recurly/account' require 'recurly/account_balance' @@ -23,7 +24,6 @@ module Recurly require 'recurly/measured_unit' require 'recurly/plan' require 'recurly/redemption' - require 'recurly/shipping_address' require 'recurly/subscription' require 'recurly/subscription_add_on' require 'recurly/transaction' diff --git a/lib/recurly/account.rb b/lib/recurly/account.rb index 6f75fedc5..210bf8e82 100644 --- a/lib/recurly/account.rb +++ b/lib/recurly/account.rb @@ -73,6 +73,8 @@ def redemption(coupon_code) has_future_subscription has_canceled_subscription has_past_due_invoice + has_paused_subscription + preferred_locale ) alias to_param account_code diff --git a/lib/recurly/adjustment.rb b/lib/recurly/adjustment.rb index a0b36c4a7..1dc688d78 100644 --- a/lib/recurly/adjustment.rb +++ b/lib/recurly/adjustment.rb @@ -21,6 +21,9 @@ class Adjustment < Resource # @return [Pager, []] has_many :credit_adjustments, class_name: :Adjustment, readonly: true + # @return [ShippingAddress, nil] + has_one :shipping_address, class_name: :ShippingAddress, readonly: false + define_attribute_methods %w( uuid state @@ -50,6 +53,7 @@ class Adjustment < Resource proration_rate credit_reason_code original_adjustment_uuid + shipping_address_id ) alias to_param uuid diff --git a/lib/recurly/api.rb b/lib/recurly/api.rb index 1099d9419..cc93ec97f 100644 --- a/lib/recurly/api.rb +++ b/lib/recurly/api.rb @@ -17,7 +17,7 @@ class API @@base_uri = "https://api.recurly.com/v2/" @@valid_domains = [".recurly.com"] - RECURLY_API_VERSION = '2.10' + RECURLY_API_VERSION = '2.11' FORMATS = Helper.hash_with_indifferent_read_access( 'pdf' => 'application/pdf', diff --git a/lib/recurly/invoice.rb b/lib/recurly/invoice.rb index 518fdb3bd..01ceabbbc 100644 --- a/lib/recurly/invoice.rb +++ b/lib/recurly/invoice.rb @@ -32,7 +32,7 @@ class Invoice < Resource # @return [Pager, []] has_many :redemptions - # @return [Pager, [ShippingAddress], []] + # @return [ShippingAddress, nil] has_one :shipping_address, class_name: :ShippingAddress, readonly: true # @return [Pager, []] diff --git a/lib/recurly/purchase.rb b/lib/recurly/purchase.rb index 4098c2e86..1a39462a1 100644 --- a/lib/recurly/purchase.rb +++ b/lib/recurly/purchase.rb @@ -19,18 +19,42 @@ module Recurly # in the same way you would when creating a {Subscription} with a new account. # # You can also pass in adjustments and invoicing data to be passed to the invoice. + # + # There are multiple ways to set the shipping addresses: + # 1. Use {Purchase#shipping_address_id} If you want to apply an existing shipping + # address to all subscriptions and adjustments in this purchase. + # 2. Add multiple shipping addresses to {Account#shipping_addresses}. The last + # address in the list will apply to all subscriptions and adjustments + # in this purchase. + # 3. Use {Subscription#shipping_address_id} or {Subscription#shipping_address} + # to set a shipping address for only the subscription. + # 4. Use {Adjustment#shipping_address_id} or {Adjustment#shipping_address} + # to set a shipping address for only the adjustment. + # # @example # require 'securerandom' # - # purchase = Recurly::Purchase.new({ + # purchase = Recurly::Purchase.new( # currency: 'USD', # collection_method: :automatic, # account: { # account_code: SecureRandom.uuid, + # shipping_addresses: [ + # { + # first_name: 'Benjamin', + # last_name: 'Du Monde', + # address1: '400 Dolores St.', + # city: 'San Francisco', + # state: 'CA', + # zip: '94110', + # country: 'US', + # nickname: 'Home' + # } + # ], # billing_info: { # first_name: 'Benjamin', # last_name: 'Du Monde', - # address1: '400 Alabama St', + # address1: '400 Alabama St.', # city: 'San Francisco', # state: 'CA', # zip: '94110', @@ -54,7 +78,7 @@ module Recurly # revenue_schedule_type: :at_invoice # } # ] - # }) + # ) # # begin # preview_invoice = Recurly::Purchase.preview!(purchase) @@ -98,6 +122,7 @@ class Purchase < Resource terms_and_conditions customer_notes vat_reverse_charge_notes + shipping_address_id ) class << self diff --git a/lib/recurly/subscription.rb b/lib/recurly/subscription.rb index 91c65a41b..46d015841 100644 --- a/lib/recurly/subscription.rb +++ b/lib/recurly/subscription.rb @@ -80,6 +80,8 @@ class Subscription < Resource no_billing_info_reason imported_trial credit_customer_notes + remaining_pause_cycles + paused_at ) alias to_param uuid @@ -220,8 +222,8 @@ def postpone next_renewal_date, bulk=false # Update the notes sections of the subscription # + # @param notes [Hash] should be the notes parameters you wish to update # @return [true, false] +true+ when successful, +false+ when unable to - # @params notes [Hash] should be the notes parameters you wish to update def update_notes(notes) return false unless link? :notes self.attributes = notes @@ -229,6 +231,40 @@ def update_notes(notes) true end + # Pauses a subscription or cancels a scheduled pause. + # + # * For an active subscription without a pause scheduled already, + # this will schedule a pause period to begin at the next renewal + # date for the specified number of billing cycles (remaining_pause_cycles). + # * When a scheduled pause already exists, this will update the remaining + # pause cycles with the new value sent. When zero (0) remaining_pause_cycles + # is sent for a subscription with a scheduled pause, the pause will be canceled. + # * For a paused subscription, the remaining_pause_cycles will adjust the + # length of the current pause period. Sending zero (0) in the remaining_pause_cycles + # field will cause the subscription to be resumed at the next renewal date. + # + # @param remaining_pause_cycles [Integer] The number of billing cycles that the subscription will be paused. + # @return true + def pause(remaining_pause_cycles) + builder = XML.new("") + builder.add_element('remaining_pause_cycles', remaining_pause_cycles) + reload API.put("#{uri}/pause", builder.to_s) + true + end + + # Resumes a paused subscription. + # + # For a paused subscription, this will immediately resume the subscription + # from the pause, produce an invoice, and return the newly resumed subscription. + # Any at-renewal subscription changes will be immediately applied + # when the subscription resumes. + # + # @return true + def resume + reload API.put("#{uri}/resume") + true + end + # Overrides the behavior of `update_attributes` in Resource class so ensure # all attributes are marked as dirty if the plan code changes def update_attributes attributes = {} diff --git a/spec/fixtures/accounts/show-200.xml b/spec/fixtures/accounts/show-200.xml index 350c29f49..8c6e42cfb 100644 --- a/spec/fixtures/accounts/show-200.xml +++ b/spec/fixtures/accounts/show-200.xml @@ -40,4 +40,5 @@ Content-Type: application/xml; charset=utf-8 false false false + fr-FR diff --git a/spec/fixtures/purchases/invoice-201.xml b/spec/fixtures/purchases/invoice-201.xml index 7427b76bc..2e550876c 100644 --- a/spec/fixtures/purchases/invoice-201.xml +++ b/spec/fixtures/purchases/invoice-201.xml @@ -4,8 +4,8 @@ Location: https://api.recurly.com/v2/invoices/3517 - - + +
400 Alabama St @@ -15,89 +15,162 @@ Location: https://api.recurly.com/v2/invoices/3517 US
- 3cd714a6322d76b0dad1d640e4a27b2c - collected + + 43d762734303e29025a8b64ef7953606 + paid - 3517 - + 5333 - 7000 0 - 7000 - 7000 + 1013 USD - 2017-04-13T17:08:12Z - 2017-04-13T17:08:12Z + 2018-03-27T17:52:42Z + 2018-03-27T17:52:42Z - 2017-04-13T17:08:12Z - - + 2018-03-27T17:52:42Z + Some notes for the customer. + 1013 + 1013 + 0 + 2018-03-27T17:52:42Z + 0 + charge + purchase + + 1013 + + 0 automatic + + Our company terms and conditions. - - - - 3cd714a628875d77f09dea4cabafe367 + + + + + + 7 + 43d762733e47a12cf3dd7c47d9aed1c4 invoiced - + Setup fee: muWlYSa6U3 - 7b3e439a-802e-4339-b37f-28a98e65ef4f - debit - 1000 + muwlysa6u3 + setup_fee + 7 1 0 0 - 1000 + 7 USD + false false - 2017-04-13T17:08:12Z + 2018-03-27T17:52:42Z - 2017-04-13T17:08:12Z - 2017-04-13T17:08:12Z - at_invoice + 2018-03-27T17:52:42Z + 2018-03-27T17:52:42Z + evenly + + 400 Alabama St + + San Francisco + CA + 94110 + US + + + + + + + + + 6 + 43d76273412274c8c041354ce7a27ee5 + invoiced + muWlYSa6U3 + + muwlysa6u3 + plan + 6 + 1 + 0 + 0 + 6 + USD + + false + false + + 2018-03-27T17:52:42Z + 2019-04-27T17:52:42Z + 2018-03-27T17:52:42Z + 2018-03-27T17:52:42Z + evenly + + 400 Alabama St + + San Francisco + CA + 94110 + US + + - - - - 3cd714a62d3c8333b9d01b4c73aee8ad + + + + + 1000 + 43d76272fc2c59081d803145bbb017e4 invoiced - bf61ee52-5c22-4f38-b06b-3ffc74e479ab + 9d138ed5-7517-4984-b03d-a2116c0a714d debit - 3000 - 2 + 1000 + 1 0 0 - 6000 + 1000 USD + false false - 2017-04-13T17:08:12Z + 2018-03-27T17:52:42Z - 2017-04-13T17:08:12Z - 2017-04-13T17:08:12Z + 2018-03-27T17:52:42Z + 2018-03-27T17:52:42Z at_invoice + + 400 Alabama St + + San Francisco + CA + 94110 + US + + - - - - 3cd714a646a4d8f4413db2433b972f3f + + + + + 43d762736a8c3f29413c0544ba92a44c purchase - 7000 + 1013 0 USD success credit_card - 3358286 - transaction + 6871449 + subscription false true true @@ -114,14 +187,14 @@ Location: https://api.recurly.com/v2/invoices/3517 Street address and postal code match. - 2017-04-13T17:08:12Z - 2017-04-13T17:08:12Z - 2017-04-13T17:08:12Z + 2018-03-27T17:52:42Z + 2018-03-27T17:52:42Z + 2018-03-27T17:52:42Z
- 9743bb2a-6b6d-4d8d-9090-df0d13405f88 - - + 7747ed3b-1771-4a03-b32a-503e9d65d3ad + Benjamin + Benjamin @@ -145,6 +218,8 @@ Location: https://api.recurly.com/v2/invoices/3517
- +
+ +
diff --git a/spec/fixtures/subscriptions/pause-200.xml b/spec/fixtures/subscriptions/pause-200.xml new file mode 100644 index 000000000..7d2365660 --- /dev/null +++ b/spec/fixtures/subscriptions/pause-200.xml @@ -0,0 +1,46 @@ +HTTP/1.1 200 OK +Content-Type: application/xml; charset=utf-8 + + + + + + + + muwlysa6u3 + muWlYSa6U3 + + evenly + abcdef1234567890 + active + 6 + USD + 1 + 2018-03-27T17:52:42Z + + + 2018-03-27T17:58:14Z + + + 2018-03-27T17:52:42Z + 2019-04-27T17:52:42Z + + + + + false + + false + 2019-04-27T17:52:42Z + 1 + + + 0 + automatic + + + + + + +
diff --git a/spec/fixtures/subscriptions/resume-200.xml b/spec/fixtures/subscriptions/resume-200.xml new file mode 100644 index 000000000..fc1c659ed --- /dev/null +++ b/spec/fixtures/subscriptions/resume-200.xml @@ -0,0 +1,46 @@ +HTTP/1.1 200 OK +Content-Type: application/xml; charset=utf-8 + + + + + + + + muwlysa6u3 + muWlYSa6U3 + + evenly + abcdef1234567890 + active + 6 + USD + 1 + 2018-03-27T17:52:42Z + + + 2018-03-27T17:58:14Z + + + 2018-03-27T17:52:42Z + 2019-04-27T17:52:42Z + + + + + false + + false + 2019-04-27T17:52:42Z + + + + 0 + automatic + + + + + + + diff --git a/spec/recurly/account_spec.rb b/spec/recurly/account_spec.rb index 44698543d..d3e350efe 100644 --- a/spec/recurly/account_spec.rb +++ b/spec/recurly/account_spec.rb @@ -110,6 +110,7 @@ account.has_active_subscription.must_equal true account.has_future_subscription.must_equal false account.has_past_due_invoice.must_equal false + account.preferred_locale.must_equal 'fr-FR' end it 'must return an account with tax state' do diff --git a/spec/recurly/purchase_spec.rb b/spec/recurly/purchase_spec.rb index 58e9c004e..6d009d404 100644 --- a/spec/recurly/purchase_spec.rb +++ b/spec/recurly/purchase_spec.rb @@ -19,6 +19,8 @@ stub_api_request(:post, 'purchases', 'purchases/invoice-201') collection = Purchase.invoice!(purchase) collection.charge_invoice.must_be_instance_of Invoice + shipping_address = collection.charge_invoice.line_items.first.shipping_address + shipping_address.must_be_instance_of ShippingAddress end it "should raise an Invalid error when data is invalid" do stub_api_request(:post, 'purchases', 'purchases/invoice-422') diff --git a/spec/recurly/subscription_spec.rb b/spec/recurly/subscription_spec.rb index 2560e0bc8..047837e46 100644 --- a/spec/recurly/subscription_spec.rb +++ b/spec/recurly/subscription_spec.rb @@ -293,6 +293,24 @@ end end + describe 'pausing and resuming' do + before do + stub_api_request :get, 'subscriptions/abcdef1234567890', 'subscriptions/show-200' + stub_api_request :put, 'https://api.recurly.com/v2/subscriptions/abcdef1234567890/pause', 'subscriptions/pause-200' + stub_api_request :put, 'https://api.recurly.com/v2/subscriptions/abcdef1234567890/resume', 'subscriptions/resume-200' + end + + it "should be able to pause and resume a subscription" do + sub = Recurly::Subscription.find('abcdef1234567890') + sub.paused_at.must_equal nil + sub.pause(1).must_equal true + sub.paused_at.must_be_instance_of DateTime + sub.remaining_pause_cycles.must_equal 1 + sub.resume.must_equal true + sub.remaining_pause_cycles.must_equal nil + end + end + describe 'notes' do it 'previews new subscriptions' do stub_api_request :get, 'subscriptions/abcdef1234567890', 'subscriptions/show-200'