From e451c4be3dd5b02ec40eeb3a99e7091e30fca0c0 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 28 Jan 2016 11:31:57 -0600 Subject: [PATCH] * ios purchase finalization --- ruby/lib/jam_ruby/models/sale.rb | 60 +++++++++++++++++-- ruby/lib/jam_ruby/models/sale_line_item.rb | 8 +++ ruby/lib/jam_ruby/models/user.rb | 8 +-- .../controllers/api_jam_tracks_controller.rb | 29 +++++---- 4 files changed, 86 insertions(+), 19 deletions(-) diff --git a/ruby/lib/jam_ruby/models/sale.rb b/ruby/lib/jam_ruby/models/sale.rb index 9561c71b5..7fb4e073a 100644 --- a/ruby/lib/jam_ruby/models/sale.rb +++ b/ruby/lib/jam_ruby/models/sale.rb @@ -69,14 +69,66 @@ module JamRuby } end + # The expectation is that this code would throw an exception (breaking the transaction that encompasses it), + # if it can't validate the receipt, or communicate with Apple at all, etc + # + # So, if this raises exceptions, you can handle them in the stubbed out begin/rescue in ApiJamTracksController#ios_order_placed + def self.validateIOSReceipt(receipt) + + # these are all 'in cents' (as painfully named to be very clear), and all expected to be integers + price_info = {subtotal_in_cents:nil, total_in_cents:nil, tax_in_cents:nil, currency: 'USD'} + + # communicate with Apple; populate price_info + + price_info + end def self.ios_purchase(current_user, jam_track, receipt) - current_user.redeem_free_credit - jam_track_right = JamRuby::JamTrackRight.find_or_create_by_user_id_and_jam_track_id(current_user.id, jam_track.id) do |jam_track_right| - jam_track_right.redeemed = false - jam_track_right.version = jam_track.version + + jam_track_right = nil + + # everything needs to go into a transaction! If anything goes wrong, we need to raise an exception to break it + Sale.transaction do + + using_free_credit = current_user.redeem_free_credit + + sale = create_jam_track_sale(current_user) + + if sale.valid? + + # JONATHAN: the 'redeem freebie' path has no communication to Apple. That's OK I assume? + if using_free_credit + SaleLineItem.create_from_jam_track(current_user, sale, jam_track, using_free_credit) + sale.recurly_subtotal_in_cents = 0 + sale.recurly_tax_in_cents = 0 + sale.recurly_total_in_cents = 0 + sale.recurly_currency = 'USD' + sale.save! + else + price_info = validateIOSReceipt(receipt) + + SaleLineItem.create_from_jam_track(current_user, sale, jam_track, using_free_credit) + + sale.recurly_subtotal_in_cents = price_info[:subtotal_in_cents] + sale.recurly_tax_in_cents = price_info[:tax_in_cents] + sale.recurly_total_in_cents = price_info[:total_in_cents] + sale.recurly_currency = price_info[:currency] + sale.save! + end + else + # should not get out of testing. This would be very rare (i.e., from a big regression). Sale is always valid at this point. + raise "invalid sale object" + end + + # if we make it this far, all is well! + jam_track_right = JamRuby::JamTrackRight.find_or_create_by_user_id_and_jam_track_id(current_user.id, jam_track.id) do |jam_track_right| + jam_track_right.redeemed = using_free_credit + jam_track_right.version = jam_track.version + end end + + jam_track_right end # place_order will create one or more sales based on the contents of shopping_carts for the current user diff --git a/ruby/lib/jam_ruby/models/sale_line_item.rb b/ruby/lib/jam_ruby/models/sale_line_item.rb index 5cbe5ad87..e105f966b 100644 --- a/ruby/lib/jam_ruby/models/sale_line_item.rb +++ b/ruby/lib/jam_ruby/models/sale_line_item.rb @@ -73,6 +73,14 @@ module JamRuby end + # in a shopping-cart less world (ios purchase), let's reuse as much logic as possible + def self.create_from_jam_track(current_user, sale, jam_track, mark_redeem) + shopping_cart = ShoppingCart.create(current_user, jam_track, 1, mark_redeem) + line_item = create_from_shopping_cart(sale, shopping_cart, nil, nil, nil) + shopping_cart.destroy + line_item + end + def self.create_from_shopping_cart(sale, shopping_cart, recurly_subscription_uuid, recurly_adjustment_uuid, recurly_adjustment_credit_uuid) product_info = shopping_cart.product_info diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 0f14c26fd..b8e99e6cd 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -1785,18 +1785,18 @@ module JamRuby end def redeem_free_credit - yn = false + using_free_credit = false if self.has_redeemable_jamtrack User.where(id: self.id).update_all(has_redeemable_jamtrack: false) self.has_redeemable_jamtrack = false - yn = true + using_free_credit = true elsif 0 < self.gifted_jamtracks User.where(id: self.id).update_all(gifted_jamtracks: self.gifted_jamtracks - 1) self.gifted_jamtracks = self.gifted_jamtracks - 1 - yn = true + using_free_credit = true end - yn + using_free_credit end private diff --git a/web/app/controllers/api_jam_tracks_controller.rb b/web/app/controllers/api_jam_tracks_controller.rb index e1b6d1bab..befcbd2ca 100644 --- a/web/app/controllers/api_jam_tracks_controller.rb +++ b/web/app/controllers/api_jam_tracks_controller.rb @@ -315,18 +315,25 @@ class ApiJamTracksController < ApiController def ios_order_placed jam_track = JamTrack.find(params[:jam_track_id]) - sales = Sale.ios_purchase(current_user, jam_track, nil) + jam_track_right = jam_track.right_for_user(current_user) + + # the user already owns this JamTrac, so just short-circuit out + if jam_track_right + response = {name: jam_track.name, id: jam_track.id, jam_track_right_id: jam_track_right.id, version: jam_track.version} + render :json => response, :status => 200 + return + end + + begin + Sale.ios_purchase(current_user, jam_track, nil) + rescue + # JONATHAN - this definitely needs beefing up so that you can communicate back to the app any errors you might raise. + # ... Go wild with this response; I just stubbed something. + response = {message:"Unable to complete purchase.", reason: nil} + render :json => response, :status => 422 + return + end - # - # sales.each do |sale| - # if sale.is_jam_track_sale? - # sale.sale_line_items.each do |line_item| - # jam_track = line_item.product - # jam_track_right = jam_track.right_for_user(current_user) - # response[:jam_tracks] << {name: jam_track.name, id: jam_track.id, jam_track_right_id: jam_track_right.id, version: jam_track.version} - # end - # end - # end response = {name: jam_track.name, id: jam_track.id, jam_track_right_id: jam_track.right_for_user(current_user).id, version: jam_track.version} render :json => response, :status => 200 end