* VRFS-2875 - sales record keeping as well as recurly hook processing
This commit is contained in:
parent
c0e541b49d
commit
821ca9d76a
|
|
@ -64,7 +64,7 @@ ActiveAdmin.register JamRuby::JamTrackRight, :as => 'JamTrackRights' do
|
|||
|
||||
begin
|
||||
client.find_or_create_account(user, billing_info)
|
||||
client.place_order(user, jam_track, nil)
|
||||
client.place_order(user, jam_track, nil, nil)
|
||||
rescue RecurlyClientError=>x
|
||||
redirect_to admin_jam_track_rights_path, notice: "Could not order #{jam_track} for #{user.to_s}: #{x.errors.inspect}"
|
||||
else
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
ActiveAdmin.register_page "Recurly Health" do
|
||||
menu :parent => 'Misc'
|
||||
|
||||
content :title => "Recurly Transaction Totals" do
|
||||
table_for Sale.check_integrity do
|
||||
column "Total", :total
|
||||
column "Unknown", :not_known
|
||||
column "Successes", :succeeded
|
||||
column "Failures", :failed
|
||||
column "Refunds", :refunded
|
||||
column "Voids", :voided
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -272,4 +272,5 @@ jam_track_id_to_varchar.sql
|
|||
drop_position_unique_jam_track.sql
|
||||
recording_client_metadata.sql
|
||||
preview_support_mp3.sql
|
||||
jam_track_duration.sql
|
||||
jam_track_duration.sql
|
||||
sales.sql
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
CREATE TABLE sales (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
user_id VARCHAR(64) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
order_total DECIMAL NOT NULL DEFAULT 0,
|
||||
shipping_info JSON,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE sale_line_items (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
product_type VARCHAR NOT NULL,
|
||||
product_id VARCHAR(64),
|
||||
unit_price DECIMAL NOT NULL,
|
||||
quantity INTEGER NOT NULL,
|
||||
free INTEGER NOT NULL,
|
||||
sales_tax DECIMAL,
|
||||
shipping_handling DECIMAL NOT NULL,
|
||||
recurly_plan_code VARCHAR NOT NULL,
|
||||
recurly_subscription_uuid VARCHAR,
|
||||
sale_id VARCHAR(64) NOT NULL REFERENCES sales(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE recurly_transaction_web_hooks (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
recurly_transaction_id VARCHAR NOT NULL,
|
||||
transaction_type VARCHAR NOT NULL,
|
||||
subscription_id VARCHAR NOT NULL,
|
||||
action VARCHAR NOT NULL,
|
||||
status VARCHAR NOT NULL,
|
||||
amount_in_cents INT,
|
||||
user_id VARCHAR(64) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
invoice_id VARCHAR,
|
||||
invoice_number_prefix VARCHAR,
|
||||
invoice_number INTEGER,
|
||||
message VARCHAR,
|
||||
reference VARCHAR,
|
||||
transaction_at TIMESTAMP NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE UNIQUE INDEX sale_line_items_recurly_subscription_uuid_ndx ON sale_line_items(recurly_subscription_uuid);
|
||||
CREATE INDEX recurly_transaction_web_hooks_subscription_id_ndx ON recurly_transaction_web_hooks(subscription_id);
|
||||
CREATE UNIQUE INDEX jam_track_rights_recurly_subscription_uuid_ndx ON jam_track_rights(recurly_subscription_uuid);
|
||||
|
||||
|
|
@ -206,6 +206,9 @@ require "jam_ruby/models/jam_company"
|
|||
require "jam_ruby/models/user_sync"
|
||||
require "jam_ruby/models/video_source"
|
||||
require "jam_ruby/models/text_message"
|
||||
require "jam_ruby/models/sale"
|
||||
require "jam_ruby/models/sale_line_item"
|
||||
require "jam_ruby/models/recurly_transaction_web_hook"
|
||||
require "jam_ruby/jam_tracks_manager"
|
||||
require "jam_ruby/jam_track_importer"
|
||||
require "jam_ruby/jmep_manager"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
module JamRuby
|
||||
class RecurlyTransactionWebHook < ActiveRecord::Base
|
||||
|
||||
belongs_to :user, class_name: 'JamRuby::User'
|
||||
|
||||
validates :recurly_transaction_id, presence: true
|
||||
validates :subscription_id, presence: true
|
||||
validates :action, presence: true
|
||||
validates :status, presence: true
|
||||
validates :amount_in_cents, numericality: {only_integer: true}
|
||||
validates :user, presence: true
|
||||
|
||||
SUCCESSFUL_PAYMENT = 'payment'
|
||||
FAILED_PAYMENT = 'failed_payment'
|
||||
REFUND = 'refund'
|
||||
VOID = 'void'
|
||||
|
||||
def self.is_transaction_web_hook?(document)
|
||||
|
||||
return false if document.root.nil?
|
||||
case document.root.name
|
||||
when 'successful_payment_notification'
|
||||
true
|
||||
when 'successful_refund_notification'
|
||||
true
|
||||
when 'failed_payment_notification'
|
||||
true
|
||||
when 'void_payment_notification'
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# see spec for examples of XML
|
||||
def self.create_from_xml(document)
|
||||
|
||||
transaction = RecurlyTransactionWebHook.new
|
||||
|
||||
case document.root.name
|
||||
when 'successful_payment_notification'
|
||||
transaction.transaction_type = SUCCESSFUL_PAYMENT
|
||||
when 'successful_refund_notification'
|
||||
transaction.transaction_type = REFUND
|
||||
when 'failed_payment_notification'
|
||||
transaction.transaction_type = FAILED_PAYMENT
|
||||
when 'void_payment_notification'
|
||||
transaction.transaction_type = VOID
|
||||
else
|
||||
raise 'unknown document type ' + document.root.name
|
||||
end
|
||||
|
||||
transaction.recurly_transaction_id = document.at_css('transaction id').content
|
||||
transaction.user_id = document.at_css('account account_code').content
|
||||
transaction.subscription_id = document.at_css('subscription_id').content
|
||||
transaction.invoice_id = document.at_css('invoice_id').content
|
||||
transaction.invoice_number_prefix = document.at_css('invoice_number_prefix').content
|
||||
transaction.invoice_number = document.at_css('invoice_number').content
|
||||
transaction.action = document.at_css('action').content
|
||||
transaction.status = document.at_css('status').content
|
||||
transaction.transaction_at = Time.parse(document.at_css('date').content)
|
||||
transaction.amount_in_cents = document.at_css('amount_in_cents').content
|
||||
transaction.reference = document.at_css('reference').content
|
||||
transaction.message = document.at_css('message').content
|
||||
|
||||
transaction.save!
|
||||
|
||||
# now that we have the transaction saved, we also need to delete the jam_track_right if this is a refund, or voided
|
||||
|
||||
if transaction.transaction_type == 'refund' || transaction.transaction_type == 'void'
|
||||
right = JamTrackRight.find_by_recurly_subscription_uuid(transaction.subscription_id)
|
||||
right.destroy if right
|
||||
end
|
||||
transaction
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
module JamRuby
|
||||
|
||||
# a sale is created every time someone tries to buy something
|
||||
class Sale < ActiveRecord::Base
|
||||
|
||||
belongs_to :user, class_name: 'JamRuby::User'
|
||||
has_many :sale_line_items, class_name: 'JamRuby::SaleLineItem'
|
||||
|
||||
validates :order_total, numericality: { only_integer: false }
|
||||
validates :user, presence: true
|
||||
|
||||
def self.create(user)
|
||||
sale = Sale.new
|
||||
sale.user = user
|
||||
sale.order_total = 0
|
||||
sale.save
|
||||
sale
|
||||
end
|
||||
|
||||
def self.check_integrity
|
||||
SaleLineItem.select([:total, :not_known, :succeeded, :failed, :refunded, :voided]).find_by_sql(
|
||||
"SELECT COUNT(sale_line_items.id) AS total,
|
||||
COUNT(CASE WHEN transactions.id IS NULL THEN 1 ELSE null END) not_known,
|
||||
COUNT(CASE WHEN transactions.transaction_type = '#{RecurlyTransactionWebHook::SUCCESSFUL_PAYMENT}' THEN 1 ELSE null END) succeeded,
|
||||
COUNT(CASE WHEN transactions.transaction_type = '#{RecurlyTransactionWebHook::FAILED_PAYMENT}' THEN 1 ELSE null END) failed,
|
||||
COUNT(CASE WHEN transactions.transaction_type = '#{RecurlyTransactionWebHook::REFUND}' THEN 1 ELSE null END) refunded,
|
||||
COUNT(CASE WHEN transactions.transaction_type = '#{RecurlyTransactionWebHook::VOID}' THEN 1 ELSE null END) voided
|
||||
FROM sale_line_items
|
||||
LEFT OUTER JOIN recurly_transaction_web_hooks as transactions ON subscription_id = recurly_subscription_uuid")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
module JamRuby
|
||||
class SaleLineItem < ActiveRecord::Base
|
||||
|
||||
belongs_to :sale, class_name: 'JamRuby::Sale'
|
||||
belongs_to :jam_track, class_name: 'JamRuby::JamTrack'
|
||||
belongs_to :jam_track_right, class_name: 'JamRuby::JamTrackRight'
|
||||
|
||||
JAMBLASTER = 'JamBlaster'
|
||||
JAMCLOUD = 'JamCloud'
|
||||
JAMTRACK = 'JamTrack'
|
||||
|
||||
validates :product_type, inclusion: {in: [JAMBLASTER, JAMCLOUD, JAMTRACK]}
|
||||
validates :unit_price, numericality: {only_integer: false}
|
||||
validates :quantity, numericality: {only_integer: true}
|
||||
validates :free, numericality: {only_integer: true}
|
||||
validates :sales_tax, numericality: {only_integer: false}, allow_nil: true
|
||||
validates :shipping_handling, numericality: {only_integer: false}
|
||||
validates :recurly_plan_code, presence:true
|
||||
validates :sale, presence:true
|
||||
|
||||
def self.create_from_shopping_cart(sale, shopping_cart, recurly_subscription_uuid)
|
||||
product_info = shopping_cart.product_info
|
||||
|
||||
sale.order_total = sale.order_total + product_info[:total_price]
|
||||
|
||||
sale_line_item = SaleLineItem.new
|
||||
sale_line_item.product_type = shopping_cart.cart_type
|
||||
sale_line_item.unit_price = product_info[:price]
|
||||
sale_line_item.quantity = product_info[:quantity]
|
||||
sale_line_item.free = product_info[:marked_for_redeem]
|
||||
sale_line_item.sales_tax = nil
|
||||
sale_line_item.shipping_handling = 0
|
||||
sale_line_item.recurly_plan_code = product_info[:plan_code]
|
||||
sale_line_item.product_id = shopping_cart.cart_id
|
||||
sale_line_item.recurly_subscription_uuid = recurly_subscription_uuid
|
||||
sale_line_item.sale = sale
|
||||
sale_line_item.save
|
||||
sale_line_item
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -159,6 +159,9 @@ module JamRuby
|
|||
# score history
|
||||
has_many :from_score_histories, :class_name => "JamRuby::ScoreHistory", foreign_key: 'from_user_id'
|
||||
has_many :to_score_histories, :class_name => "JamRuby::ScoreHistory", foreign_key: 'to_user_id'
|
||||
has_many :sales, :class_name => 'JamRuby::Sale', dependent: :destroy
|
||||
has_many :recurly_transaction_web_hooks, :class_name => 'JamRuby::RecurlyTransactionWebHook', dependent: :destroy
|
||||
|
||||
# This causes the authenticate method to be generated (among other stuff)
|
||||
#has_secure_password
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
require 'recurly'
|
||||
module JamRuby
|
||||
class RecurlyClient
|
||||
def initialize()
|
||||
def initialize()
|
||||
@log = Logging.logger[self]
|
||||
end
|
||||
|
||||
def create_account(current_user, billing_info)
|
||||
|
|
@ -66,7 +67,8 @@ module JamRuby
|
|||
if(account.present?)
|
||||
begin
|
||||
account.transactions.find_each do |transaction|
|
||||
if transaction.amount_in_cents > 0 # Account creation adds a transaction record
|
||||
# XXX this isn't correct because we create 0 dollar transactions too (for free stuff)
|
||||
#if transaction.amount_in_cents > 0 # Account creation adds a transaction record
|
||||
payments << {
|
||||
:created_at => transaction.created_at,
|
||||
:amount_in_cents => transaction.amount_in_cents,
|
||||
|
|
@ -74,7 +76,7 @@ module JamRuby
|
|||
:payment_method => transaction.payment_method,
|
||||
:reference => transaction.reference
|
||||
}
|
||||
end
|
||||
#end
|
||||
end
|
||||
rescue Recurly::Error, NoMethodError => x
|
||||
raise RecurlyClientError, x.to_s
|
||||
|
|
@ -175,7 +177,7 @@ module JamRuby
|
|||
raise RecurlyClientError.new(plan.errors) if plan.errors.any?
|
||||
end
|
||||
|
||||
def place_order(current_user, jam_track, shopping_cart)
|
||||
def place_order(current_user, jam_track, shopping_cart, sale)
|
||||
jam_track_right = nil
|
||||
account = get_account(current_user)
|
||||
if (account.present?)
|
||||
|
|
@ -203,29 +205,30 @@ module JamRuby
|
|||
|
||||
raise RecurlyClientError.new(subscription.errors) if subscription.errors.any?
|
||||
|
||||
# add a line item for the sale
|
||||
sale_line_item = SaleLineItem.create_from_shopping_cart(sale, shopping_cart, subscription.uuid)
|
||||
|
||||
unless sale_line_item.valid?
|
||||
@log.error("sale item invalid! #{sale_line_item.errors.inspect}")
|
||||
puts("sale item invalid! #{sale_line_item.errors.inspect}")
|
||||
Stats.write('web.recurly.purchase.sale_invalid', {message: sale.errors.to_s, value:1})
|
||||
end
|
||||
|
||||
# delete from shopping cart the subscription
|
||||
shopping_cart.destroy if shopping_cart
|
||||
|
||||
# Reload and make sure it went through:
|
||||
account = get_account(current_user)
|
||||
|
||||
account.subscriptions.find_each do |subscription|
|
||||
if subscription.plan.plan_code == jam_track.plan_code
|
||||
recurly_subscription_uuid = subscription.uuid
|
||||
break
|
||||
end
|
||||
end
|
||||
recurly_subscription_uuid = subscription.uuid
|
||||
end
|
||||
|
||||
raise RecurlyClientError, "Plan code '#{paid_subscription.plan_code}' doesn't match jam track: '#{jam_track.plan_code}'" unless recurly_subscription_uuid
|
||||
#raise RecurlyClientError, "Plan code '#{paid_subscription.plan_code}' doesn't match jam track: '#{jam_track.plan_code}'" unless recurly_subscription_uuid
|
||||
|
||||
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 = free
|
||||
end
|
||||
|
||||
# also if the purchase was a free one, then update the user record to no longer allow redeemed jamtracks
|
||||
User.where(id: current_user.id).update_all(has_redeemable_jamtrack: false) if free
|
||||
|
||||
# also if the purchase was a free one, then update the user record to no longer allow redeemed jamtracks
|
||||
# this can't go in the block above, as it's here to fix bad subscription UUIDs in an update path
|
||||
if jam_track_right.recurly_subscription_uuid != recurly_subscription_uuid
|
||||
jam_track_right.recurly_subscription_uuid = recurly_subscription_uuid
|
||||
|
|
|
|||
|
|
@ -769,4 +769,30 @@ FactoryGirl.define do
|
|||
bpm 120
|
||||
tap_in_count 3
|
||||
end
|
||||
|
||||
factory :sale, :class => JamRuby::Sale do
|
||||
order_total 0
|
||||
association :user, factory:user
|
||||
end
|
||||
|
||||
factory :recurly_transaction_web_hook, :class => JamRuby::RecurlyTransactionWebHook do
|
||||
|
||||
transaction_type JamRuby::RecurlyTransactionWebHook::SUCCESSFUL_PAYMENT
|
||||
sequence(:recurly_transaction_id ) { |n| "recurly-transaction-id-#{n}" }
|
||||
sequence(:subscription_id ) { |n| "subscription-id-#{n}" }
|
||||
sequence(:invoice_id ) { |n| "invoice-id-#{n}" }
|
||||
sequence(:invoice_number ) { |n| 1000 + n }
|
||||
invoice_number_prefix nil
|
||||
action 'purchase'
|
||||
status 'success'
|
||||
transaction_at Time.now
|
||||
amount_in_cents 199
|
||||
reference 100000
|
||||
message 'meh'
|
||||
association :user, factory: :user
|
||||
|
||||
factory :recurly_transaction_web_hook_failed do
|
||||
transaction_type JamRuby::RecurlyTransactionWebHook::FAILED_PAYMENT
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,244 @@
|
|||
require 'spec_helper'
|
||||
|
||||
# verifies that all webhooks work, except for the failed_payment_notification hook, since I don't have an example of it.
|
||||
# because the other 3 types work, I feel pretty confident it will work
|
||||
|
||||
# testing with CURL:
|
||||
# curl -X POST -d @filename.txt http://localhost:3000/api/recurly/webhook --header "Content-Type:text/xml" --user monkeytoesspeartoss:frizzyfloppymushface
|
||||
# where @filename.txt is either empty (creates no row), or the contents of one of the create_from_xml tests below (replacing the account_code with a real user_id in our system)
|
||||
|
||||
describe RecurlyTransactionWebHook do
|
||||
|
||||
let(:refund_xml) {'<?xml version="1.0" encoding="UTF-8"?>
|
||||
<successful_refund_notification>
|
||||
<account>
|
||||
<account_code>56d5b2c6-2a4b-46e4-a984-ec1fbe83a50d</account_code>
|
||||
<username nil="true"></username>
|
||||
<email>sethcall@gmail.com</email>
|
||||
<first_name>Seth</first_name>
|
||||
<last_name>Call</last_name>
|
||||
<company_name nil="true"></company_name>
|
||||
</account>
|
||||
<transaction>
|
||||
<id>2de439790e8fceb7fc385a4a89b89883</id>
|
||||
<invoice_id>2da71ad9c657adf9fe618e4f058c78bb</invoice_id>
|
||||
<invoice_number_prefix></invoice_number_prefix>
|
||||
<invoice_number type="integer">1033</invoice_number>
|
||||
<subscription_id>2da71ad97c826a7b784c264ac59c04de</subscription_id>
|
||||
<action>refund</action>
|
||||
<date type="datetime">2015-04-01T14:41:40Z</date>
|
||||
<amount_in_cents type="integer">216</amount_in_cents>
|
||||
<status>success</status>
|
||||
<message>Successful test transaction</message>
|
||||
<reference>3819545</reference>
|
||||
<source>subscription</source>
|
||||
<cvv_result code=""></cvv_result>
|
||||
<avs_result code="D">Street address and postal code match.</avs_result>
|
||||
<avs_result_street nil="true"></avs_result_street>
|
||||
<avs_result_postal nil="true"></avs_result_postal>
|
||||
<test type="boolean">true</test>
|
||||
<voidable type="boolean">true</voidable>
|
||||
<refundable type="boolean">false</refundable>
|
||||
</transaction>
|
||||
</successful_refund_notification>'
|
||||
}
|
||||
|
||||
let(:void_xml) {
|
||||
'<?xml version="1.0" encoding="UTF-8"?>
|
||||
<void_payment_notification>
|
||||
<account>
|
||||
<account_code>56d5b2c6-2a4b-46e4-a984-ec1fbe83a50d</account_code>
|
||||
<username nil="true"></username>
|
||||
<email>sethcall@gmail.com</email>
|
||||
<first_name>Seth</first_name>
|
||||
<last_name>Call</last_name>
|
||||
<company_name nil="true"></company_name>
|
||||
</account>
|
||||
<transaction>
|
||||
<id>2de4370332f709c768313d4f47a9af1d</id>
|
||||
<invoice_id>2da71ad9c657adf9fe618e4f058c78bb</invoice_id>
|
||||
<invoice_number_prefix></invoice_number_prefix>
|
||||
<invoice_number type="integer">1033</invoice_number>
|
||||
<subscription_id>2da71ad97c826a7b784c264ac59c04de</subscription_id>
|
||||
<action>refund</action>
|
||||
<date type="datetime">2015-04-01T14:38:59Z</date>
|
||||
<amount_in_cents type="integer">216</amount_in_cents>
|
||||
<status>void</status>
|
||||
<message>Successful test transaction</message>
|
||||
<reference>3183996</reference>
|
||||
<source>subscription</source>
|
||||
<cvv_result code=""></cvv_result>
|
||||
<avs_result code="D">Street address and postal code match.</avs_result>
|
||||
<avs_result_street nil="true"></avs_result_street>
|
||||
<avs_result_postal nil="true"></avs_result_postal>
|
||||
<test type="boolean">true</test>
|
||||
<voidable type="boolean">false</voidable>
|
||||
<refundable type="boolean">false</refundable>
|
||||
</transaction>
|
||||
</void_payment_notification>'
|
||||
}
|
||||
|
||||
let(:success_xml) {
|
||||
'<?xml version="1.0" encoding="UTF-8"?>
|
||||
<successful_payment_notification>
|
||||
<account>
|
||||
<account_code>56d5b2c6-2a4b-46e4-a984-ec1fbe83a50d</account_code>
|
||||
<username nil="true"></username>
|
||||
<email>seth@jamkazam.com</email>
|
||||
<first_name>Seth</first_name>
|
||||
<last_name>Call</last_name>
|
||||
<company_name nil="true"></company_name>
|
||||
</account>
|
||||
<transaction>
|
||||
<id>2de4448533db12d6d92b4c4b4e90a4f1</id>
|
||||
<invoice_id>2de44484fa4528b504555f43ac8bf42f</invoice_id>
|
||||
<invoice_number_prefix></invoice_number_prefix>
|
||||
<invoice_number type="integer">1037</invoice_number>
|
||||
<subscription_id>2de44484b460d95863799a431383b165</subscription_id>
|
||||
<action>purchase</action>
|
||||
<date type="datetime">2015-04-01T14:53:44Z</date>
|
||||
<amount_in_cents type="integer">216</amount_in_cents>
|
||||
<status>success</status>
|
||||
<message>Successful test transaction</message>
|
||||
<reference>6249355</reference>
|
||||
<source>subscription</source>
|
||||
<cvv_result code=""></cvv_result>
|
||||
<avs_result code="D">Street address and postal code match.</avs_result>
|
||||
<avs_result_street nil="true"></avs_result_street>
|
||||
<avs_result_postal nil="true"></avs_result_postal>
|
||||
<test type="boolean">true</test>
|
||||
<voidable type="boolean">true</voidable>
|
||||
<refundable type="boolean">true</refundable>
|
||||
</transaction>
|
||||
</successful_payment_notification>'
|
||||
}
|
||||
describe "sales integrity maintanence" do
|
||||
|
||||
before(:each) do
|
||||
@user = FactoryGirl.create(:user, id: '56d5b2c6-2a4b-46e4-a984-ec1fbe83a50d')
|
||||
end
|
||||
|
||||
it "deletes jam_track_right when refunded" do
|
||||
|
||||
# create a jam_track right, which should be whacked as soon as we craete the web hook
|
||||
jam_track_right = FactoryGirl.create(:jam_track_right, user: @user, recurly_subscription_uuid: '2da71ad97c826a7b784c264ac59c04de')
|
||||
|
||||
document = Nokogiri::XML(refund_xml)
|
||||
|
||||
RecurlyTransactionWebHook.create_from_xml(document)
|
||||
|
||||
JamTrackRight.find_by_id(jam_track_right.id).should be_nil
|
||||
end
|
||||
|
||||
it "deletes jam_track_right when voided" do
|
||||
# create a jam_track right, which should be whacked as soon as we craete the web hook
|
||||
jam_track_right = FactoryGirl.create(:jam_track_right, user: @user, recurly_subscription_uuid: '2da71ad97c826a7b784c264ac59c04de')
|
||||
|
||||
document = Nokogiri::XML(void_xml)
|
||||
|
||||
RecurlyTransactionWebHook.create_from_xml(document)
|
||||
|
||||
JamTrackRight.find_by_id(jam_track_right.id).should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
describe "is_transaction_web_hook?" do
|
||||
|
||||
it "successful payment" do
|
||||
document = Nokogiri::XML('<?xml version="1.0" encoding="UTF-8"?><successful_payment_notification/>')
|
||||
RecurlyTransactionWebHook.is_transaction_web_hook?(document).should be_true
|
||||
end
|
||||
|
||||
it "successful refund" do
|
||||
document = Nokogiri::XML('<?xml version="1.0" encoding="UTF-8"?><successful_refund_notification/>')
|
||||
RecurlyTransactionWebHook.is_transaction_web_hook?(document).should be_true
|
||||
end
|
||||
|
||||
it "failed payment" do
|
||||
document = Nokogiri::XML('<?xml version="1.0" encoding="UTF-8"?><failed_payment_notification/>')
|
||||
RecurlyTransactionWebHook.is_transaction_web_hook?(document).should be_true
|
||||
end
|
||||
|
||||
it "void" do
|
||||
document = Nokogiri::XML('<?xml version="1.0" encoding="UTF-8"?><void_payment_notification/>')
|
||||
RecurlyTransactionWebHook.is_transaction_web_hook?(document).should be_true
|
||||
end
|
||||
|
||||
it "not a transaction web hook" do
|
||||
document = Nokogiri::XML('<?xml version="1.0" encoding="UTF-8"?><something/>')
|
||||
RecurlyTransactionWebHook.is_transaction_web_hook?(document).should be_false
|
||||
end
|
||||
end
|
||||
describe "create_from_xml" do
|
||||
|
||||
before(:each) do
|
||||
@user = FactoryGirl.create(:user, id: '56d5b2c6-2a4b-46e4-a984-ec1fbe83a50d')
|
||||
end
|
||||
|
||||
it "successful payment" do
|
||||
|
||||
document = Nokogiri::XML(success_xml)
|
||||
|
||||
transaction = RecurlyTransactionWebHook.create_from_xml(document)
|
||||
transaction.valid?.should be_true
|
||||
transaction.user.should eq(@user)
|
||||
transaction.transaction_type.should eq('payment')
|
||||
transaction.subscription_id.should eq('2de44484b460d95863799a431383b165')
|
||||
transaction.invoice_id.should eq('2de44484fa4528b504555f43ac8bf42f')
|
||||
transaction.invoice_number_prefix.should eq('')
|
||||
transaction.invoice_number.should eq(1037)
|
||||
transaction.recurly_transaction_id.should eq('2de4448533db12d6d92b4c4b4e90a4f1')
|
||||
transaction.action.should eq('purchase')
|
||||
transaction.transaction_at.should eq(Time.parse('2015-04-01T14:53:44Z'))
|
||||
transaction.amount_in_cents.should eq(216)
|
||||
transaction.status.should eq('success')
|
||||
transaction.message.should eq('Successful test transaction')
|
||||
transaction.reference.should eq('6249355')
|
||||
end
|
||||
|
||||
it "successful refund" do
|
||||
document = Nokogiri::XML(refund_xml)
|
||||
|
||||
transaction = RecurlyTransactionWebHook.create_from_xml(document)
|
||||
transaction.valid?.should be_true
|
||||
transaction.user.should eq(@user)
|
||||
transaction.transaction_type.should eq('refund')
|
||||
transaction.subscription_id.should eq('2da71ad97c826a7b784c264ac59c04de')
|
||||
transaction.invoice_id.should eq('2da71ad9c657adf9fe618e4f058c78bb')
|
||||
transaction.invoice_number_prefix.should eq('')
|
||||
transaction.invoice_number.should eq(1033)
|
||||
transaction.recurly_transaction_id.should eq('2de439790e8fceb7fc385a4a89b89883')
|
||||
transaction.action.should eq('refund')
|
||||
transaction.transaction_at.should eq(Time.parse('2015-04-01T14:41:40Z'))
|
||||
transaction.amount_in_cents.should eq(216)
|
||||
transaction.status.should eq('success')
|
||||
transaction.message.should eq('Successful test transaction')
|
||||
transaction.reference.should eq('3819545')
|
||||
|
||||
end
|
||||
|
||||
it "successful void" do
|
||||
document = Nokogiri::XML(void_xml)
|
||||
|
||||
transaction = RecurlyTransactionWebHook.create_from_xml(document)
|
||||
transaction.valid?.should be_true
|
||||
transaction.user.should eq(@user)
|
||||
transaction.transaction_type.should eq('void')
|
||||
transaction.subscription_id.should eq('2da71ad97c826a7b784c264ac59c04de')
|
||||
transaction.invoice_id.should eq('2da71ad9c657adf9fe618e4f058c78bb')
|
||||
transaction.invoice_number_prefix.should eq('')
|
||||
transaction.invoice_number.should eq(1033)
|
||||
transaction.recurly_transaction_id.should eq('2de4370332f709c768313d4f47a9af1d')
|
||||
transaction.action.should eq('refund')
|
||||
transaction.transaction_at.should eq(Time.parse('2015-04-01T14:38:59Z'))
|
||||
transaction.amount_in_cents.should eq(216)
|
||||
transaction.status.should eq('void')
|
||||
transaction.message.should eq('Successful test transaction')
|
||||
transaction.reference.should eq('3183996')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# https://github.com/killbilling/recurly-java-library/blob/master/src/main/java/com/ning/billing/recurly/model/push/payment/FailedPaymentNotification.java
|
||||
# failed_payment_notification
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Sale do
|
||||
|
||||
describe "check_integrity" do
|
||||
|
||||
let(:user) {FactoryGirl.create(:user)}
|
||||
let(:jam_track) {FactoryGirl.create(:jam_track)}
|
||||
|
||||
it "empty" do
|
||||
check_integrity = Sale.check_integrity
|
||||
check_integrity.length.should eq(1)
|
||||
r = check_integrity[0]
|
||||
r.total.to_i.should eq(0)
|
||||
r.not_known.to_i.should eq(0)
|
||||
r.succeeded.to_i.should eq(0)
|
||||
r.failed.to_i.should eq(0)
|
||||
r.refunded.to_i.should eq(0)
|
||||
r.voided.to_i.should eq(0)
|
||||
end
|
||||
|
||||
it "one unknown sale" do
|
||||
sale = Sale.create(user)
|
||||
shopping_cart = ShoppingCart.create(user, jam_track)
|
||||
SaleLineItem.create_from_shopping_cart(sale, shopping_cart, 'some_recurly_uuid')
|
||||
|
||||
check_integrity = Sale.check_integrity
|
||||
r = check_integrity[0]
|
||||
r.total.to_i.should eq(1)
|
||||
r.not_known.to_i.should eq(1)
|
||||
r.succeeded.to_i.should eq(0)
|
||||
r.failed.to_i.should eq(0)
|
||||
r.refunded.to_i.should eq(0)
|
||||
r.voided.to_i.should eq(0)
|
||||
end
|
||||
|
||||
it "one succeeded sale" do
|
||||
sale = Sale.create(user)
|
||||
shopping_cart = ShoppingCart.create(user, jam_track)
|
||||
SaleLineItem.create_from_shopping_cart(sale, shopping_cart, 'some_recurly_uuid')
|
||||
FactoryGirl.create(:recurly_transaction_web_hook, subscription_id: 'some_recurly_uuid')
|
||||
|
||||
|
||||
check_integrity = Sale.check_integrity
|
||||
r = check_integrity[0]
|
||||
r.total.to_i.should eq(1)
|
||||
r.not_known.to_i.should eq(0)
|
||||
r.succeeded.to_i.should eq(1)
|
||||
r.failed.to_i.should eq(0)
|
||||
r.refunded.to_i.should eq(0)
|
||||
r.voided.to_i.should eq(0)
|
||||
end
|
||||
|
||||
it "one failed sale" do
|
||||
sale = Sale.create(user)
|
||||
shopping_cart = ShoppingCart.create(user, jam_track)
|
||||
SaleLineItem.create_from_shopping_cart(sale, shopping_cart, 'some_recurly_uuid')
|
||||
FactoryGirl.create(:recurly_transaction_web_hook_failed, subscription_id: 'some_recurly_uuid')
|
||||
|
||||
check_integrity = Sale.check_integrity
|
||||
r = check_integrity[0]
|
||||
r.total.to_i.should eq(1)
|
||||
r.not_known.to_i.should eq(0)
|
||||
r.succeeded.to_i.should eq(0)
|
||||
r.failed.to_i.should eq(1)
|
||||
r.refunded.to_i.should eq(0)
|
||||
r.voided.to_i.should eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -2,7 +2,6 @@ require 'spec_helper'
|
|||
require "jam_ruby/recurly_client"
|
||||
describe RecurlyClient do
|
||||
let(:jamtrack) { FactoryGirl.create(:jam_track, plan_code: 'jamtrack-acdc-backinblack') }
|
||||
#let(:client) { RecurlyClient.new }
|
||||
|
||||
before :all do
|
||||
@client = RecurlyClient.new
|
||||
|
|
@ -88,23 +87,48 @@ describe RecurlyClient do
|
|||
end
|
||||
|
||||
it "can place order" do
|
||||
sale = Sale.create(@user)
|
||||
sale = Sale.find(sale.id)
|
||||
shopping_cart = ShoppingCart.create @user, @jamtrack, 1, true
|
||||
history_items = @client.payment_history(@user).length
|
||||
@client.find_or_create_account(@user, @billing_info)
|
||||
expect{@client.place_order(@user, @jamtrack, nil)}.not_to raise_error()
|
||||
expect{@client.place_order(@user, @jamtrack, shopping_cart, sale)}.not_to raise_error()
|
||||
|
||||
# verify jam_track_rights data
|
||||
@user.jam_track_rights.should_not be_nil
|
||||
@user.jam_track_rights.should have(1).items
|
||||
@user.jam_track_rights.last.jam_track.id.should eq(@jamtrack.id)
|
||||
|
||||
# verify sales data
|
||||
sale = Sale.find(sale.id)
|
||||
sale.sale_line_items.length.should == 1
|
||||
sale_line_item = sale.sale_line_items[0]
|
||||
sale_line_item.product_type.should eq(JamTrack::PRODUCT_TYPE)
|
||||
sale_line_item.unit_price.should eq(@jamtrack.price)
|
||||
sale_line_item.quantity.should eq(1)
|
||||
sale_line_item.free.should eq(1)
|
||||
sale_line_item.sales_tax.should be_nil
|
||||
sale_line_item.shipping_handling.should eq(0)
|
||||
sale_line_item.recurly_plan_code.should eq(@jamtrack.plan_code)
|
||||
sale_line_item.product_id.should eq(@jamtrack.id)
|
||||
sale_line_item.recurly_subscription_uuid.should_not be_nil
|
||||
sale_line_item.recurly_subscription_uuid.should eq(@user.jam_track_rights.last.recurly_subscription_uuid)
|
||||
|
||||
# verify subscription is in Recurly
|
||||
subs = @client.get_account(@user).subscriptions
|
||||
subs.should_not be_nil
|
||||
subs.should have(1).items
|
||||
@user.jam_track_rights.should_not be_nil
|
||||
@user.jam_track_rights.should have(1).items
|
||||
@user.jam_track_rights.last.jam_track.id.should eq(@jamtrack.id)
|
||||
|
||||
@client.payment_history(@user).should have(history_items+1).items
|
||||
end
|
||||
|
||||
it "can refund subscription" do
|
||||
@client.find_or_create_account(@user, @billing_info)
|
||||
sale = Sale.create(@user)
|
||||
shopping_cart = ShoppingCart.create @user, @jamtrack, 1
|
||||
@client.find_or_create_account(@user, @billing_info)
|
||||
|
||||
# Place order:
|
||||
expect{@client.place_order(@user, @jamtrack, nil)}.not_to raise_error()
|
||||
expect{@client.place_order(@user, @jamtrack, shopping_cart, sale)}.not_to raise_error()
|
||||
active_subs=@client.get_account(@user).subscriptions.find_all{|t|t.state=='active'}
|
||||
@jamtrack.reload
|
||||
@jamtrack.jam_track_rights.should have(1).items
|
||||
|
|
@ -119,11 +143,14 @@ describe RecurlyClient do
|
|||
end
|
||||
|
||||
it "detects error on double order" do
|
||||
sale = Sale.create(@user)
|
||||
shopping_cart = ShoppingCart.create @user, @jamtrack, 1
|
||||
@client.find_or_create_account(@user, @billing_info)
|
||||
jam_track_right = @client.place_order(@user, @jamtrack, nil)
|
||||
jam_track_right = @client.place_order(@user, @jamtrack, shopping_cart, sale)
|
||||
jam_track_right.recurly_subscription_uuid.should_not be_nil
|
||||
|
||||
jam_track_right2 = @client.place_order(@user, @jamtrack, nil)
|
||||
shopping_cart = ShoppingCart.create @user, @jamtrack, 1
|
||||
jam_track_right2 = @client.place_order(@user, @jamtrack, shopping_cart, sale)
|
||||
jam_track_right.should eq(jam_track_right2)
|
||||
jam_track_right.recurly_subscription_uuid.should eq(jam_track_right.recurly_subscription_uuid)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ input[type="button"] {
|
|||
}
|
||||
|
||||
.hidden {
|
||||
display:none !important;
|
||||
display:none;
|
||||
}
|
||||
|
||||
.small {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ body.web.landing_page {
|
|||
margin:0 0 5px;
|
||||
padding:7px 0;
|
||||
display:inline-block;
|
||||
|
||||
&.hidden {
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
.row {
|
||||
@include border_box_sizing;
|
||||
|
|
|
|||
|
|
@ -123,16 +123,22 @@ class ApiRecurlyController < ApiController
|
|||
error=nil
|
||||
response = {jam_tracks:[]}
|
||||
|
||||
current_user.shopping_carts.each do |shopping_cart|
|
||||
jam_track = shopping_cart.cart_product
|
||||
sale = Sale.create(current_user)
|
||||
|
||||
# if shopping_cart has any marked_for_redeem, then we zero out the price by passing in 'free'
|
||||
# NOTE: shopping_carts have the idea of quantity, but you should only be able to buy at most one JamTrack. So anything > 0 is considered free for a JamTrack
|
||||
if sale.valid?
|
||||
current_user.shopping_carts.each do |shopping_cart|
|
||||
jam_track = shopping_cart.cart_product
|
||||
|
||||
jam_track_right = @client.place_order(current_user, jam_track, shopping_cart)
|
||||
# build up the response object with JamTracks that were purchased.
|
||||
# if this gets more complicated, we should switch to RABL
|
||||
response[:jam_tracks] << {name: jam_track.name, id: jam_track.id, jam_track_right_id: jam_track_right.id, version: jam_track.version}
|
||||
# if shopping_cart has any marked_for_redeem, then we zero out the price by passing in 'free'
|
||||
# NOTE: shopping_carts have the idea of quantity, but you should only be able to buy at most one JamTrack. So anything > 0 is considered free for a JamTrack
|
||||
|
||||
jam_track_right = @client.place_order(current_user, jam_track, shopping_cart, sale)
|
||||
# build up the response object with JamTracks that were purchased.
|
||||
# if this gets more complicated, we should switch to RABL
|
||||
response[:jam_tracks] << {name: jam_track.name, id: jam_track.id, jam_track_right_id: jam_track_right.id, version: jam_track.version}
|
||||
end
|
||||
else
|
||||
error = 'can not create sale'
|
||||
end
|
||||
|
||||
if error
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
class ApiRecurlyWebHookController < ApiController
|
||||
|
||||
http_basic_authenticate_with name: Rails.application.config.recurly_webhook_user, password: Rails.application.config.recurly_webhook_pass
|
||||
|
||||
before_filter :api_signed_in_user, only: []
|
||||
#respond_to :xml
|
||||
|
||||
|
||||
def on_hook
|
||||
begin
|
||||
|
||||
document = Nokogiri::XML(request.body)
|
||||
|
||||
if RecurlyTransactionWebHook.is_transaction_web_hook?(document)
|
||||
transaction = RecurlyTransactionWebHook.create_from_xml(document)
|
||||
end
|
||||
|
||||
rescue Exception => e
|
||||
Stats.write('web.recurly.webhook.transaction.error', {message: e.to_s, value: 1})
|
||||
|
||||
log.error("unable to process webhook: #{e.to_s}")
|
||||
|
||||
raise JamArgumentError.new("unable to parse webhook #{e.to_s}")
|
||||
end
|
||||
|
||||
Stats.write('web.recurly.webhook.transaction.success', {value: 1})
|
||||
|
||||
render xml: { success: true }, :status => 200
|
||||
end
|
||||
end
|
||||
|
|
@ -182,6 +182,9 @@ if defined?(Bundler)
|
|||
config.bugsnag_key = "4289fc981c8ce3eb0969003c4f498b01"
|
||||
config.bugsnag_notify_release_stages = ["production"] # add 'development' if you want to test a bugsnag feature locally
|
||||
|
||||
config.recurly_webhook_user = 'monkeytoesspeartoss'
|
||||
config.recurly_webhook_pass = 'frizzyfloppymushface'
|
||||
|
||||
config.ga_ua = 'UA-44184562-2' # google analytics
|
||||
config.ga_endpoint = 'www.google-analytics.com'
|
||||
config.ga_ua_version = '1'
|
||||
|
|
|
|||
|
|
@ -552,6 +552,9 @@ SampleApp::Application.routes.draw do
|
|||
|
||||
# latency_tester
|
||||
match '/latency_testers' => 'api_latency_testers#match', :via => :get
|
||||
|
||||
match '/recurly/webhook' => 'api_recurly_web_hook#on_hook', :via => :post
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
require 'spec_helper'
|
||||
require 'jam_ruby/recurly_client'
|
||||
|
||||
describe ApiRecurlyWebHookController, :type=>:request do
|
||||
render_views
|
||||
|
||||
let(:success_xml) {
|
||||
'<?xml version="1.0" encoding="UTF-8"?>
|
||||
<successful_payment_notification>
|
||||
<account>
|
||||
<account_code>56d5b2c6-2a4b-46e4-a984-ec1fbe83a50d</account_code>
|
||||
<username nil="true"></username>
|
||||
<email>seth@jamkazam.com</email>
|
||||
<first_name>Seth</first_name>
|
||||
<last_name>Call</last_name>
|
||||
<company_name nil="true"></company_name>
|
||||
</account>
|
||||
<transaction>
|
||||
<id>2de4448533db12d6d92b4c4b4e90a4f1</id>
|
||||
<invoice_id>2de44484fa4528b504555f43ac8bf42f</invoice_id>
|
||||
<invoice_number_prefix></invoice_number_prefix>
|
||||
<invoice_number type="integer">1037</invoice_number>
|
||||
<subscription_id>2de44484b460d95863799a431383b165</subscription_id>
|
||||
<action>purchase</action>
|
||||
<date type="datetime">2015-04-01T14:53:44Z</date>
|
||||
<amount_in_cents type="integer">216</amount_in_cents>
|
||||
<status>success</status>
|
||||
<message>Successful test transaction</message>
|
||||
<reference>6249355</reference>
|
||||
<source>subscription</source>
|
||||
<cvv_result code=""></cvv_result>
|
||||
<avs_result code="D">Street address and postal code match.</avs_result>
|
||||
<avs_result_street nil="true"></avs_result_street>
|
||||
<avs_result_postal nil="true"></avs_result_postal>
|
||||
<test type="boolean">true</test>
|
||||
<voidable type="boolean">true</voidable>
|
||||
<refundable type="boolean">true</refundable>
|
||||
</transaction>
|
||||
</successful_payment_notification>'
|
||||
}
|
||||
|
||||
let(:no_user_xml) {
|
||||
'<?xml version="1.0" encoding="UTF-8"?>
|
||||
<successful_payment_notification>
|
||||
<account>
|
||||
<account_code>HUHUHUHUHUHUHUHU</account_code>
|
||||
<username nil="true"></username>
|
||||
<email>seth@jamkazam.com</email>
|
||||
<first_name>Seth</first_name>
|
||||
<last_name>Call</last_name>
|
||||
<company_name nil="true"></company_name>
|
||||
</account>
|
||||
<transaction>
|
||||
<id>2de4448533db12d6d92b4c4b4e90a4f1</id>
|
||||
<invoice_id>2de44484fa4528b504555f43ac8bf42f</invoice_id>
|
||||
<invoice_number_prefix></invoice_number_prefix>
|
||||
<invoice_number type="integer">1037</invoice_number>
|
||||
<subscription_id>2de44484b460d95863799a431383b165</subscription_id>
|
||||
<action>purchase</action>
|
||||
<date type="datetime">2015-04-01T14:53:44Z</date>
|
||||
<amount_in_cents type="integer">216</amount_in_cents>
|
||||
<status>success</status>
|
||||
<message>Successful test transaction</message>
|
||||
<reference>6249355</reference>
|
||||
<source>subscription</source>
|
||||
<cvv_result code=""></cvv_result>
|
||||
<avs_result code="D">Street address and postal code match.</avs_result>
|
||||
<avs_result_street nil="true"></avs_result_street>
|
||||
<avs_result_postal nil="true"></avs_result_postal>
|
||||
<test type="boolean">true</test>
|
||||
<voidable type="boolean">true</voidable>
|
||||
<refundable type="boolean">true</refundable>
|
||||
</transaction>
|
||||
</successful_payment_notification>'
|
||||
}
|
||||
|
||||
before(:all) do
|
||||
User.delete_all
|
||||
@user = FactoryGirl.create(:user, id: '56d5b2c6-2a4b-46e4-a984-ec1fbe83a50d')
|
||||
end
|
||||
|
||||
it "no auth" do
|
||||
request.env['RAW_POST_DATA'] = success_xml
|
||||
@request.env['RAW_POST_DATA'] = success_xml
|
||||
post :on_hook, {}, { 'CONTENT_TYPE' => 'application/xml', 'ACCEPT' => 'application/xml' }
|
||||
response.status.should eq(401)
|
||||
end
|
||||
|
||||
it "succeeds" do
|
||||
@request.env['RAW_POST_DATA'] = success_xml
|
||||
@request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64(Rails.application.config.recurly_webhook_user + ":" + Rails.application.config.recurly_webhook_pass )
|
||||
post :on_hook, {}, { 'Content-Type' => 'application/xml' }
|
||||
response.status.should eq(200)
|
||||
end
|
||||
|
||||
it "returns 422 on error" do
|
||||
@request.env['RAW_POST_DATA'] = no_user_xml
|
||||
@request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64(Rails.application.config.recurly_webhook_user + ":" + Rails.application.config.recurly_webhook_pass )
|
||||
post :on_hook, {}, { 'Content-Type' => 'application/xml' }
|
||||
response.status.should eq(422)
|
||||
end
|
||||
|
||||
it "returns 200 for unknown hook event" do
|
||||
@request.env['RAW_POST_DATA'] = '<meh/>'
|
||||
@request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64(Rails.application.config.recurly_webhook_user + ":" + Rails.application.config.recurly_webhook_pass )
|
||||
post :on_hook, {}, { 'Content-Type' => 'application/xml' }
|
||||
response.status.should eq(200)
|
||||
end
|
||||
end
|
||||
|
|
@ -47,6 +47,7 @@ describe "Checkout", :js => true, :type => :feature, :capybara_feature => true d
|
|||
|
||||
before(:each) do
|
||||
ShoppingCart.delete_all
|
||||
Sale.delete_all
|
||||
User.delete_all
|
||||
|
||||
stub_const("APP_CONFIG", web_config)
|
||||
|
|
@ -545,6 +546,28 @@ describe "Checkout", :js => true, :type => :feature, :capybara_feature => true d
|
|||
acdc.redeemed.should be_false
|
||||
pearljam = jamtrack_pearljam_evenflow.right_for_user(user)
|
||||
pearljam.redeemed.should be_false
|
||||
|
||||
# verify sales data
|
||||
user.sales.length.should eq(1)
|
||||
sale = user.sales.first
|
||||
sale.sale_line_items.length.should eq(2)
|
||||
|
||||
acdc_sale = SaleLineItem.find_by_recurly_subscription_uuid(acdc.recurly_subscription_uuid)
|
||||
acdc_sale.recurly_plan_code.should eq(jamtrack_acdc_backinblack.plan_code)
|
||||
acdc_sale.product_type.should eq('JamTrack')
|
||||
acdc_sale.product_id.should eq(jamtrack_acdc_backinblack.id)
|
||||
acdc_sale.quantity.should eq(1)
|
||||
acdc_sale.free.should eq(0)
|
||||
acdc_sale.unit_price.should eq(1.99)
|
||||
acdc_sale.sale.should eq(sale)
|
||||
pearljam_sale = SaleLineItem.find_by_recurly_subscription_uuid(pearljam.recurly_subscription_uuid)
|
||||
pearljam_sale.recurly_plan_code.should eq(jamtrack_pearljam_evenflow.plan_code)
|
||||
pearljam_sale.product_type.should eq('JamTrack')
|
||||
pearljam_sale.product_id.should eq(jamtrack_pearljam_evenflow.id)
|
||||
pearljam_sale.quantity.should eq(1)
|
||||
pearljam_sale.free.should eq(0)
|
||||
pearljam_sale.unit_price.should eq(1.99)
|
||||
pearljam_sale.sale.should eq(sale)
|
||||
end
|
||||
|
||||
it "shows purchase error correctly" do
|
||||
|
|
@ -633,6 +656,20 @@ describe "Checkout", :js => true, :type => :feature, :capybara_feature => true d
|
|||
jam_track_right.redeemed.should be_true
|
||||
guy.has_redeemable_jamtrack.should be_false
|
||||
|
||||
# verify sales data
|
||||
guy.sales.length.should eq(1)
|
||||
sale = guy.sales.first
|
||||
sale.sale_line_items.length.should eq(1)
|
||||
acdc_sale = SaleLineItem.find_by_recurly_subscription_uuid(jam_track_right.recurly_subscription_uuid)
|
||||
acdc_sale.recurly_plan_code.should eq(jamtrack_acdc_backinblack.plan_code)
|
||||
acdc_sale.product_type.should eq('JamTrack')
|
||||
acdc_sale.product_id.should eq(jamtrack_acdc_backinblack.id)
|
||||
acdc_sale.quantity.should eq(1)
|
||||
acdc_sale.free.should eq(1)
|
||||
acdc_sale.unit_price.should eq(1.99)
|
||||
acdc_sale.sale.should eq(sale)
|
||||
|
||||
|
||||
# now, go back to checkout flow again, and make sure we are told there are no free jam tracks
|
||||
|
||||
visit "/client#/jamtrack"
|
||||
|
|
@ -661,12 +698,27 @@ describe "Checkout", :js => true, :type => :feature, :capybara_feature => true d
|
|||
# and now we should see confirmation, and a notice that we are in a normal browser
|
||||
find('.thanks-detail.jam-tracks-in-browser')
|
||||
|
||||
guy.reload
|
||||
|
||||
jam_track_right = jamtrack_pearljam_evenflow.right_for_user(guy)
|
||||
# make sure it appears the user actually bought the jamtrack!
|
||||
jam_track_right.should_not be_nil
|
||||
jam_track_right.redeemed.should be_false
|
||||
guy.has_redeemable_jamtrack.should be_false
|
||||
|
||||
# verify sales data
|
||||
guy.sales.length.should eq(2)
|
||||
sale = guy.sales.last
|
||||
sale.sale_line_items.length.should eq(1)
|
||||
acdc_sale = SaleLineItem.find_by_recurly_subscription_uuid(jam_track_right.recurly_subscription_uuid)
|
||||
acdc_sale.recurly_plan_code.should eq(jamtrack_pearljam_evenflow.plan_code)
|
||||
acdc_sale.product_type.should eq('JamTrack')
|
||||
acdc_sale.product_id.should eq(jamtrack_pearljam_evenflow.id)
|
||||
acdc_sale.quantity.should eq(1)
|
||||
acdc_sale.free.should eq(0)
|
||||
acdc_sale.unit_price.should eq(1.99)
|
||||
acdc_sale.sale.should eq(sale)
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue