pause
This commit is contained in:
parent
9476141a6c
commit
2ec522a366
|
|
@ -56,4 +56,27 @@ ALTER TABLE users ADD COLUMN beta BOOLEAN default FALSE;
|
|||
ALTER TABLE arses ADD COLUMN beta BOOLEAN default FALSE;
|
||||
|
||||
|
||||
ALTER TABLE generic_state ADD COLUMN event_page_top_logo_url VARCHAR(100000) DEFAULT '/assets/event/eventbrite-logo.png';
|
||||
ALTER TABLE generic_state ADD COLUMN event_page_top_logo_url VARCHAR(100000) DEFAULT '/assets/event/eventbrite-logo.png';
|
||||
|
||||
ALTER TABLE users ADD COLUMN recurly_subscription_id VARCHAR(100) DEFAULT NULL;
|
||||
ALTER TABLE users ADD COLUMN recurly_token VARCHAR(200) DEFAULT NULL;
|
||||
ALTER TABLE users ADD COLUMN recurly_subscription_state VARCHAR(20) DEFAULT NULL;
|
||||
|
||||
|
||||
CREATE TABLE subscriptions (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
name VARCHAR(200) UNIQUE NOT NULL UNIQUE NOT NULL,
|
||||
play_time_per_session_mins INT DEFAULT NULL,
|
||||
play_time_per_month_mins INT DEFAULT NULL,
|
||||
can_record BOOLEAN DEFAULT TRUE,
|
||||
audio_max_bitrate INT DEFAULT NULL,
|
||||
save_as_wave BOOLEAN DEFAULT FALSE,
|
||||
pro_audio BOOLEAN DEFAULT FALSE,
|
||||
video_resolution VARCHAR(50) DEFAULT NULL,
|
||||
broadcasting_type VARCHAR(50) DEFAULT NULL,
|
||||
music_lessons VARCHAR(50) DEFAULT NULL,
|
||||
support VARCHAR(50) DEFAULT NULL,
|
||||
max_players_per_session INT DEFAULT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
|
@ -6,6 +6,7 @@ module JamRuby
|
|||
JAMTRACK_SALE = 'jamtrack'
|
||||
LESSON_SALE = 'lesson'
|
||||
POSA_SALE = 'posacard'
|
||||
SUBSCRIPTION_SALE = 'subscription'
|
||||
|
||||
SOURCE_RECURLY = 'recurly'
|
||||
SOURCE_IOS = 'ios'
|
||||
|
|
@ -260,6 +261,41 @@ module JamRuby
|
|||
{sale: sale}
|
||||
end
|
||||
|
||||
def self.purchase_subscription(current_user, recurly_token, plan_code)
|
||||
sale = nil
|
||||
|
||||
Sale.transaction(:requires_new => true) do
|
||||
|
||||
current_user.recurly_token = recurly_token
|
||||
current_user.subscription_plan_code = plan_code
|
||||
|
||||
sale = create_subscription_sale(current_user)
|
||||
|
||||
if sale.valid?
|
||||
|
||||
client = RecurlyClient.new
|
||||
account = client.get_account(current_user)
|
||||
|
||||
if account.present?
|
||||
recurly_response = client.create_subscription(current_user, plan_code, account)
|
||||
current_user.recurly_subscription_id = recurly_response.uuid
|
||||
current_user.save(validate: false)
|
||||
SaleLineItem.create_from_subscription(current_user, sale, plan_code, recurly_response)
|
||||
|
||||
sale.recurly_subtotal_in_cents = recurly_response.unit_amount_in_cents
|
||||
sale.recurly_tax_in_cents = recurly_response.tax_in_cents
|
||||
sale.recurly_total_in_cents = sale.recurly_subtotal_in_cents + sale.recurly_tax_in_cents
|
||||
sale.recurly_currency = recurly_response.currency
|
||||
sale.save(validate: false)
|
||||
else
|
||||
raise RecurlyClientError, "Could not find account to place order."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
{sale: sale}
|
||||
end
|
||||
|
||||
# this is easy to make generic, but right now, it just purchases lessons
|
||||
def self.purchase_lesson(charge, current_user, lesson_booking, lesson_package_type, lesson_session = nil, lesson_package_purchase = nil, force = false, posa_card = nil)
|
||||
stripe_charge = nil
|
||||
|
|
@ -776,7 +812,16 @@ module JamRuby
|
|||
def self.create_lesson_sale(user)
|
||||
sale = Sale.new
|
||||
sale.user = user
|
||||
sale.sale_type = LESSON_SALE # gift cards and jam tracks are sold with this type of sale
|
||||
sale.sale_type = LESSON_SALE
|
||||
sale.order_total = 0
|
||||
sale.save
|
||||
sale
|
||||
end
|
||||
|
||||
def self.create_subscription_sale(user)
|
||||
sale = Sale.new
|
||||
sale.user = user
|
||||
sale.sale_type = SUBSCRIPTION_SALE
|
||||
sale.order_total = 0
|
||||
sale.save
|
||||
sale
|
||||
|
|
@ -785,7 +830,7 @@ module JamRuby
|
|||
def self.create_posa_sale(retailer, posa_card)
|
||||
sale = Sale.new
|
||||
sale.retailer = retailer
|
||||
sale.sale_type = POSA_SALE # gift cards and jam tracks are sold with this type of sale
|
||||
sale.sale_type = POSA_SALE
|
||||
sale.order_total = posa_card.product_info[:price]
|
||||
sale.save
|
||||
sale
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ module JamRuby
|
|||
GIFTCARD = 'GiftCardType'
|
||||
LESSON = 'LessonPackageType'
|
||||
POSACARD = 'PosaCard'
|
||||
SUBSCRIPTION = 'Subscription'
|
||||
|
||||
belongs_to :sale, class_name: 'JamRuby::Sale'
|
||||
belongs_to :jam_track, class_name: 'JamRuby::JamTrack'
|
||||
|
|
@ -22,7 +23,7 @@ module JamRuby
|
|||
|
||||
has_many :recurly_transactions, class_name: 'JamRuby::RecurlyTransactionWebHook', inverse_of: :sale_line_item, foreign_key: 'subscription_id', primary_key: 'recurly_subscription_uuid'
|
||||
|
||||
validates :product_type, inclusion: {in: [JAMBLASTER, JAMCLOUD, JAMTRACK, GIFTCARD, LESSON, POSACARD]}
|
||||
validates :product_type, inclusion: {in: [JAMBLASTER, JAMCLOUD, JAMTRACK, GIFTCARD, LESSON, POSACARD, SUBSCRIPTION]}
|
||||
validates :unit_price, numericality: {only_integer: false}
|
||||
validates :quantity, numericality: {only_integer: true}
|
||||
validates :free, numericality: {only_integer: true}
|
||||
|
|
@ -40,9 +41,15 @@ module JamRuby
|
|||
product_type == GIFTCARD
|
||||
end
|
||||
|
||||
def is_subscription?
|
||||
product_type == SUBSCRIPTION
|
||||
end
|
||||
|
||||
def product
|
||||
if product_type == JAMTRACK
|
||||
JamTrack.find_by_id(product_id)
|
||||
if product_type == SUBSCRIPTION
|
||||
{name: product_id}
|
||||
elsif product_type == GIFTCARD
|
||||
GiftCardType.find_by_id(product_id)
|
||||
elsif product_type == LESSON
|
||||
|
|
@ -105,6 +112,24 @@ module JamRuby
|
|||
self.save!
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def self.create_from_subscription(current_user, sale, plan_code, recurly_response)
|
||||
sale_line_item = SaleLineItem.new
|
||||
sale_line_item.product_type = SUBSCRIPTION
|
||||
sale_line_item.product_id = recurly_response.uuid
|
||||
sale_line_item.unit_price = recurly_response.unit_amount_in_cents
|
||||
sale_line_item.quantity = 1
|
||||
sale_line_item.free = false
|
||||
sale_line_item.sales_tax = recurly_response.tax_in_cents
|
||||
sale_line_item.shipping_handling = 0
|
||||
sale_line_item.recurly_plan_code = plan_code
|
||||
|
||||
sale.sale_line_items << sale_line_item
|
||||
sale_line_item.save
|
||||
sale_line_item
|
||||
end
|
||||
|
||||
# in a shopping-cart less world (ios purchase), let's reuse as much logic as possible
|
||||
def self.create_from_lesson_package(current_user, sale, lesson_package_type, lesson_booking)
|
||||
teacher = lesson_booking.teacher if lesson_booking
|
||||
|
|
|
|||
|
|
@ -186,6 +186,44 @@ module JamRuby
|
|||
raise RecurlyClientError.new(plan.errors) if plan.errors.any?
|
||||
end
|
||||
|
||||
# https://dev.recurly.com/docs/create-subscription
|
||||
def create_subscription(user, plan_code, account)
|
||||
subscription = Recurly::Subscription.create(
|
||||
:plan_code => plan_code,
|
||||
:currency => 'USD',
|
||||
:customer_notes => 'Thank you for your business!',
|
||||
:account => {
|
||||
:account_code => account.account_code
|
||||
},
|
||||
:auto_renew => true
|
||||
)
|
||||
subscription
|
||||
end
|
||||
|
||||
def find_subscription(user)
|
||||
if user.recurly_subscription_id.nil?
|
||||
nil
|
||||
else
|
||||
Recurly::Subscription.find(user.recurly_subscription_id)
|
||||
end
|
||||
end
|
||||
|
||||
def sync_subscription(user)
|
||||
subscription = find_subscription(user)
|
||||
|
||||
if subscription.nil?
|
||||
if user.subscription_plan_code
|
||||
user.subscription_plan_code = nil
|
||||
user.recurly_subscription_state = nil
|
||||
user.save(validate:false)
|
||||
end
|
||||
else
|
||||
user.recurly_subscription_state = subscription.state
|
||||
if user.subscription_plan_code != subscription.plan.plan_code
|
||||
user.subscription_plan_code = subscription.plan.plan_code
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def find_or_create_account(current_user, billing_info)
|
||||
account = get_account(current_user)
|
||||
|
|
|
|||
|
|
@ -1,21 +1,35 @@
|
|||
context = window
|
||||
rest = context.JK.Rest()
|
||||
logger = context.JK.logger
|
||||
|
||||
LocationActions = context.LocationActions
|
||||
UserStore = context.UserStore
|
||||
|
||||
@Subscription = React.createClass({
|
||||
|
||||
|
||||
mixins: [Reflux.listenTo(@LocationStore, "onLocationsChanged")]
|
||||
|
||||
|
||||
getInitialState: () ->
|
||||
{
|
||||
clicked: false
|
||||
clicked: false,
|
||||
selectedCountry: null
|
||||
}
|
||||
|
||||
onLocationsChanged: (countries) ->
|
||||
console.log("countires in ", countries)
|
||||
@setState({countries: countries})
|
||||
|
||||
onCountryChanged: (e) ->
|
||||
val = $(e.target).val()
|
||||
@setState({selectedCountry: val})
|
||||
|
||||
currentCountry: () ->
|
||||
this.state.selectedCountry || this.props.selectedCountry || ''
|
||||
|
||||
openBrowser: () ->
|
||||
context.JK.popExternalLink("https://www.jamkazam.com/client#/subscription")
|
||||
|
||||
|
||||
onRecurlyToken: (err, token) ->
|
||||
console.log("TOKEN", token)
|
||||
if err
|
||||
|
|
@ -38,6 +52,8 @@ UserStore = context.UserStore
|
|||
window.configuredRecurly = true
|
||||
|
||||
componentDidMount: () ->
|
||||
LocationActions.load()
|
||||
|
||||
@configureRecurly()
|
||||
|
||||
@elements = recurly.Elements()
|
||||
|
|
@ -67,32 +83,48 @@ UserStore = context.UserStore
|
|||
document.querySelector('#subscription-form').addEventListener('submit', @onFormSubmit.bind(this))
|
||||
|
||||
|
||||
defaultText: () ->
|
||||
'Select Country'
|
||||
|
||||
render: () ->
|
||||
|
||||
if @state.countries?
|
||||
countries = [`<option key="" value="">{this.defaultText()}</option>`]
|
||||
for countryId, countryInfo of @state.countries
|
||||
countries.push(`<option key={countryId} value={countryId}>{countryInfo.name}</option>`)
|
||||
|
||||
country = @state.countries[this.currentCountry()]
|
||||
else
|
||||
countries = []
|
||||
|
||||
countryJsx = `
|
||||
<select name="countries" onChange={this.onCountryChanged} value={this.currentCountry()} data-recurly="country" autocomplete="shipping country">{countries}</select>`
|
||||
|
||||
`<form id="subscription-form">
|
||||
<div id="subscription-account-data">
|
||||
<label for="first_name">First Name:</label>
|
||||
<input type="text" data-recurly="first_name"></input>
|
||||
<input type="text" data-recurly="first_name" required autocomplete="cc-give-name"></input>
|
||||
|
||||
<label for="last_name">Last Name:</label>
|
||||
<input type="text" data-recurly="last_name"></input>
|
||||
<input type="text" data-recurly="last_name" required autocomplete="cc-family-name"></input>
|
||||
|
||||
<label for="address1">Address 1:</label>
|
||||
<input type="text" data-recurly="address1"></input>
|
||||
<input type="text" data-recurly="address1" required autocomplete="shipping address-line1"></input>
|
||||
|
||||
<label for="address2">Address 2:</label>
|
||||
<input type="text" data-recurly="address2"></input>
|
||||
<input type="text" data-recurly="address2" required autocomplete="shipping address-line1"></input>
|
||||
|
||||
<label for="city">City:</label>
|
||||
<input type="text" data-recurly="city"></input>
|
||||
<input type="text" data-recurly="city" required autocomplete="shipping address-level2"></input>
|
||||
|
||||
<label for="state">State:</label>
|
||||
<input type="text" data-recurly="state"></input>
|
||||
<input type="text" data-recurly="state" required autocomplete="shipping address-level1"></input>
|
||||
|
||||
<label for="postal_code">Postal Code:</label>
|
||||
<input type="text" data-recurly="postal_code"></input>
|
||||
<input type="text" data-recurly="postal_code" autocomplete="shipping postal-code"></input>
|
||||
|
||||
<label for="country">Country:</label>
|
||||
<input type="text" data-recurly="country"></input>
|
||||
{countryJsx}
|
||||
</div>
|
||||
<div id="subscription-elements">
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
input[type="text"], select {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
border: 2px solid #c2c2c2;
|
||||
border: 1px solid #c2c2c2;
|
||||
background: white;
|
||||
padding: 0.5rem;
|
||||
margin: 0 0 1rem;
|
||||
|
|
@ -64,7 +64,7 @@
|
|||
font-weight: bold;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
color: #c2c2c2;
|
||||
color: black;
|
||||
-webkit-appearance: none;
|
||||
-webkit-transition: border-color 0.3s;
|
||||
-moz-transition: border-color 0.3s;
|
||||
|
|
@ -74,8 +74,9 @@
|
|||
}
|
||||
|
||||
input:focus {
|
||||
border-color: #2c0730;
|
||||
color: #2c0730;
|
||||
border: 1px solid white;
|
||||
color: black;
|
||||
background:#d2d2d2;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
|
|
@ -84,7 +85,7 @@
|
|||
}
|
||||
|
||||
div.error .recurly-hosted-field {
|
||||
border: 2px solid #e43c29;
|
||||
border: 1px solid #c2c2c2;
|
||||
}
|
||||
|
||||
@media screen and (max-height: 599px) {
|
||||
|
|
|
|||
|
|
@ -124,6 +124,57 @@ class ApiRecurlyController < ApiController
|
|||
render json: {message: x.inspect, errors: x.errors}, :status => 404
|
||||
end
|
||||
|
||||
def create_subscriptions
|
||||
begin
|
||||
sale = Sale.purchase_subscription(current_user, params[:recurly_token], params[:plan_code])
|
||||
subscription = Recurly::Subscription.find(current_user.recurly_subscription_id)
|
||||
render :json => subscription.to_json
|
||||
rescue RecurlyClientError => x
|
||||
render json: {:message => x.inspect, errors: x.errors}, :status => 404
|
||||
end
|
||||
end
|
||||
|
||||
def get_subscription
|
||||
subscription_id = current_user.recurly_subscription_id
|
||||
subscription = Recurly::Subscription.find(subscription_id) if subscription_id
|
||||
|
||||
if subscription
|
||||
render :json => subscription
|
||||
else
|
||||
render :json => {}
|
||||
end
|
||||
end
|
||||
|
||||
def cancel_subscription
|
||||
begin
|
||||
@client.cancel_subscription(current_user.recurly_subscription_id)
|
||||
subscription = Recurly::Subscription.find(current_user.recurly_subscription_id)
|
||||
render :json => subscription.to_json
|
||||
rescue RecurlyClientError => x
|
||||
render json: {:message => x.inspect, errors: x.errors}, :status => 404
|
||||
end
|
||||
end
|
||||
|
||||
def change_subscription_plan
|
||||
begin
|
||||
@client.change_subscription_plan(current_user.recurly_subscription_id, params[:plan_code])
|
||||
subscription = Recurly::Subscription.find(current_user.recurly_subscription_id)
|
||||
render :json => subscription.to_json
|
||||
rescue RecurlyClientError => x
|
||||
render json: {:message => x.inspect, errors: x.errors}, :status => 404
|
||||
end
|
||||
end
|
||||
|
||||
def change_subscription_payment
|
||||
begin
|
||||
@client.change_subscription_payment(current_user.recurly_subscription_id, params[:recurly_token], params[:billing_ifo])
|
||||
subscription = Recurly::Subscription.find(current_user.recurly_subscription_id)
|
||||
render :json => subscription.to_json
|
||||
rescue RecurlyClientError => x
|
||||
render json: {:message => x.inspect, errors: x.errors}, :status => 404
|
||||
end
|
||||
end
|
||||
|
||||
def place_order
|
||||
error=nil
|
||||
response = {jam_tracks: [], gift_cards: []}
|
||||
|
|
|
|||
Loading…
Reference in New Issue