445 lines
20 KiB
Ruby
445 lines
20 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# License: AGPL-3.0-or-later WITH WTO-AP-3.0-or-later
|
|
# Full license explanation at https://github.com/houdiniproject/houdini/blob/master/LICENSE
|
|
require 'rails_helper'
|
|
describe InsertCard do
|
|
describe '.with_stripe' do
|
|
let(:stripe_helper) { StripeMock.create_test_helper }
|
|
|
|
let(:stripe_card_token) { StripeMock.generate_card_token(last4: '9191', exp_year: 2011) }
|
|
let(:default_card_attribs) do
|
|
{
|
|
created_at: Time.now,
|
|
updated_at: Time.now,
|
|
profile_id: nil,
|
|
status: nil,
|
|
inactive: nil,
|
|
deleted: nil,
|
|
expiration_month: nil,
|
|
expiration_year: nil,
|
|
email: nil,
|
|
supporter_id: nil
|
|
}
|
|
end
|
|
|
|
let(:nonprofit) { force_create(:nm_justice) }
|
|
let(:user) do
|
|
user = force_create(:user)
|
|
force_create(:role, name: :nonprofit_admin, host: nonprofit, user: user)
|
|
user
|
|
end
|
|
|
|
around(:each) do |example|
|
|
Timecop.freeze(2025, 5, 4) do
|
|
StripeMock.start
|
|
example.run
|
|
StripeMock.stop
|
|
end
|
|
end
|
|
|
|
it 'params are invalid' do
|
|
ret = InsertCard.with_stripe({})
|
|
expect(ret[:status]).to eq(:unprocessable_entity)
|
|
expect(ret[:json][:error]).to start_with('Validation error')
|
|
expect(ret[:json][:errors].length).to be(9)
|
|
|
|
expect_validation_errors(ret[:json][:errors], [{ key: 'holder_id', name: :required },
|
|
{ key: 'holder_type', name: 'included_in' },
|
|
{ key: 'holder_type', name: 'required' },
|
|
{ key: 'stripe_card_id', name: 'required' },
|
|
{ key: 'stripe_card_id', name: 'not_blank' },
|
|
{ key: 'stripe_card_token', name: 'required' },
|
|
{ key: 'stripe_card_token', name: 'not_blank' },
|
|
{ key: 'name', name: 'required' },
|
|
{ key: 'name', name: 'not_blank' }])
|
|
end
|
|
|
|
describe 'for nonprofits' do
|
|
let(:supporter) { nonprofit.supporters.first }
|
|
it 'nonprofit doesn\'t exist' do
|
|
ret = InsertCard.with_stripe(holder_id: 3, holder_type: 'Nonprofit', stripe_card_id: 'card_fafjeht', stripe_card_token: stripe_card_token, name: 'name')
|
|
expect(ret[:status]).to eq(:unprocessable_entity)
|
|
expect(ret[:json][:error]).to include('Sorry, you need to provide a nonprofit or supporter')
|
|
end
|
|
|
|
it 'should properly add nonprofit card when no card exists' do
|
|
stripe_customer = nil
|
|
expect(Stripe::Customer).to receive(:create).and_wrap_original { |m, *args| stripe_customer = m.call(*args); stripe_customer }
|
|
card_data = { holder_type: 'Nonprofit', holder_id: nonprofit.id, stripe_card_id: 'card_88888', stripe_card_token: stripe_card_token, name: 'card_name' }
|
|
orig_card = nonprofit.active_card
|
|
expect(orig_card).to be_nil
|
|
card_ret = InsertCard.with_stripe(card_data)
|
|
nonprofit.reload
|
|
card = nonprofit.active_card
|
|
|
|
compare_card_returned_to_real(card_ret, card)
|
|
|
|
expected_card = {
|
|
id: card.id,
|
|
name: 'card_name',
|
|
stripe_card_token: stripe_card_token,
|
|
stripe_card_id: 'card_88888',
|
|
holder_type: 'Nonprofit',
|
|
holder_id: nonprofit.id,
|
|
stripe_customer_id: stripe_customer['id']
|
|
}.merge(default_card_attribs).with_indifferent_access
|
|
|
|
expect(card.attributes).to eq expected_card
|
|
|
|
expect(Card.where('holder_id = ? and holder_type = ?', nonprofit.id, 'Nonprofit').count).to eq(1)
|
|
|
|
customer = verify_cust_added_np(card.stripe_customer_id, nonprofit.id)
|
|
expect(customer.sources.count).to eq(1)
|
|
expect(customer.sources.data[0].object).to eq('card')
|
|
expect(customer.sources.data[0].last4).to eq('9191')
|
|
expect(customer.sources.data[0].exp_year).to eq(2011)
|
|
end
|
|
|
|
it 'invalid params get ignored' do
|
|
stripe_customer = nil
|
|
expect(Stripe::Customer).to receive(:create).and_wrap_original { |m, *args| stripe_customer = m.call(*args); stripe_customer }
|
|
card_data = { holder_type: 'Nonprofit', holder_id: nonprofit.id, stripe_card_id: 'card_88888', stripe_card_token: stripe_card_token,
|
|
name: 'card_name', created_at: DateTime.new(0), updated_at: DateTime.new(0), inactive: true }
|
|
|
|
card_ret = InsertCard.with_stripe(card_data)
|
|
|
|
nonprofit.reload
|
|
card = Card.find(card_ret[:json][:id])
|
|
|
|
expect(nonprofit.active_card).to eq card
|
|
compare_card_returned_to_real(card_ret, card)
|
|
|
|
expected_card = {
|
|
id: card.id,
|
|
name: 'card_name',
|
|
stripe_card_token: stripe_card_token,
|
|
stripe_card_id: 'card_88888',
|
|
holder_type: 'Nonprofit',
|
|
holder_id: nonprofit.id,
|
|
stripe_customer_id: stripe_customer['id']
|
|
}.merge(default_card_attribs).with_indifferent_access
|
|
|
|
expect(card.attributes).to eq expected_card
|
|
end
|
|
|
|
describe 'card exists' do
|
|
before(:each) do
|
|
@first_card_tok = StripeMock.generate_card_token(last4: '9999', exp_year: '2122')
|
|
@stripe_customer = Stripe::Customer.create
|
|
@stripe_customer.sources.create(token: @first_card_tok)
|
|
end
|
|
|
|
it 'should properly add nonprofit card and make old inactive' do
|
|
stripe_customer = nil
|
|
expect(Stripe::Customer).to receive(:create).and_wrap_original { |m, *args| stripe_customer = m.call(*args); stripe_customer }
|
|
first_card = nonprofit.create_active_card(stripe_card_id: 'fake mcfake', stripe_card_token: @first_card_tok, name: 'fake name')
|
|
|
|
card_data = { holder_type: 'Nonprofit', holder_id: nonprofit.id, stripe_card_id: 'card_88888', stripe_card_token: stripe_card_token, name: 'card_name' }
|
|
card_ret = InsertCard.with_stripe(card_data)
|
|
|
|
nonprofit.reload
|
|
card = nonprofit.active_card
|
|
|
|
compare_card_returned_to_real(card_ret, card)
|
|
|
|
expected_card = {
|
|
id: card.id,
|
|
name: 'card_name',
|
|
stripe_card_token: stripe_card_token,
|
|
stripe_card_id: 'card_88888',
|
|
holder_type: 'Nonprofit',
|
|
holder_id: nonprofit.id,
|
|
stripe_customer_id: stripe_customer['id']
|
|
}.merge(default_card_attribs).with_indifferent_access
|
|
|
|
expect(card.attributes).to eq expected_card
|
|
expect(Card.where('holder_id = ? and holder_type = ?', nonprofit.id, 'Nonprofit').count).to eq(2)
|
|
expect(Card.where('holder_id = ? and holder_type = ? and inactive != ?', nonprofit.id, 'Nonprofit', false).count).to eq(1)
|
|
|
|
customer = verify_cust_added_np(card.stripe_customer_id, nonprofit.id)
|
|
|
|
expect(customer.sources.count).to eq(1)
|
|
expect(customer.sources.data.any? { |s| s.object == 'card' && s.last4 == '9191' && s.exp_year == 2011 }).to eq(true)
|
|
|
|
# verify the original card didn't change
|
|
expect(nonprofit.cards.find(first_card.id).attributes.reject { |k, _| k == 'inactive' }).to eq first_card.attributes.reject { |k, _| k == 'inactive' }
|
|
|
|
expect(nonprofit.cards.find(first_card.id).inactive).to eq true
|
|
end
|
|
end
|
|
|
|
it 'handle card errors' do
|
|
StripeMock.prepare_error(Stripe::CardError.new('card error', nil, nil, 300, {}, error: { message: 'a message' }), :new_customer)
|
|
card_data = { holder_type: 'Nonprofit', holder_id: nonprofit.id, stripe_card_id: 'card_88888', stripe_card_token: stripe_card_token, name: 'card_name' }
|
|
|
|
card_ret = InsertCard.with_stripe(card_data)
|
|
|
|
expect(card_ret[:status]).to be :unprocessable_entity
|
|
expect(card_ret[:json][:error]).to start_with('Oops!')
|
|
end
|
|
|
|
it 'handle stripe errors' do
|
|
StripeMock.prepare_error(Stripe::StripeError.new('card error', nil, nil), :new_customer)
|
|
card_data = { holder_type: 'Nonprofit', holder_id: nonprofit.id, stripe_card_id: 'card_88888', stripe_card_token: stripe_card_token, name: 'card_name' }
|
|
|
|
card_ret = InsertCard.with_stripe(card_data)
|
|
|
|
expect(card_ret[:status]).to eq :unprocessable_entity
|
|
expect(card_ret[:json][:error]).to start_with('Oops!')
|
|
end
|
|
|
|
def verify_cust_added_np(stripe_customer_id, holder_id)
|
|
verify_cust_added(stripe_customer_id, holder_id, 'Nonprofit')
|
|
end
|
|
end
|
|
|
|
describe 'for supporter' do
|
|
let(:supporter) { force_create(:supporter, nonprofit: nonprofit) }
|
|
let(:event) do
|
|
force_create(:event, nonprofit: nonprofit, end_datetime: Time.now.since(1.day))
|
|
end
|
|
let(:user_not_from_nonprofit) { force_create(:user) }
|
|
def verify_cust_added_supporter(stripe_customer_id, holder_id)
|
|
verify_cust_added(stripe_customer_id, holder_id, 'Supporter')
|
|
end
|
|
|
|
def verify_supporter_source_token(source_token, card)
|
|
verify_source_token(source_token, card, 1, Time.now.since(20.minutes))
|
|
end
|
|
|
|
def verify_event_source_token(source_token, card, event)
|
|
verify_source_token(source_token, card, 20, event.end_datetime.since(20.days), event)
|
|
end
|
|
|
|
context 'card exists' do
|
|
let(:supporter) { create(:supporter, :has_a_card, nonprofit: nonprofit) }
|
|
|
|
it 'should properly add supporter card' do
|
|
expect(supporter.cards.count).to eq(1)
|
|
stripe_customer = nil
|
|
expect(Stripe::Customer).to receive(:create).and_wrap_original { |m, *args| stripe_customer = m.call(*args); stripe_customer }
|
|
card_data = { holder_type: 'Supporter', holder_id: supporter.id, stripe_card_id: 'card_88888', stripe_card_token: stripe_card_token, name: 'card_name' }
|
|
orig_card = supporter.cards.first
|
|
card_ret = InsertCard.with_stripe(card_data)
|
|
supporter.reload
|
|
card = supporter.cards.where('cards.name = ?', 'card_name').first
|
|
compare_card_returned_to_real(card_ret, card, card_ret[:json]['token'])
|
|
|
|
expected_card = {
|
|
id: card.id,
|
|
name: 'card_name',
|
|
stripe_card_token: stripe_card_token,
|
|
stripe_card_id: 'card_88888',
|
|
holder_id: supporter.id,
|
|
holder_type: 'Supporter',
|
|
stripe_customer_id: stripe_customer['id']
|
|
}.merge(default_card_attribs).with_indifferent_access
|
|
|
|
expect(card.attributes).to eq expected_card
|
|
|
|
expect(supporter.cards.count).to eq(2)
|
|
|
|
expect(Card.where('holder_id = ? and holder_type = ?', supporter.id, 'Supporter').count).to eq(2)
|
|
expect(Card.where('holder_id = ? and holder_type = ? and inactive != ?', supporter.id, 'Supporter', false).count).to eq(0)
|
|
|
|
expect(supporter.cards.find(orig_card.id)).to eq(orig_card)
|
|
|
|
verify_cust_added_supporter(card.stripe_customer_id, supporter.id)
|
|
|
|
verify_supporter_source_token(card_ret[:json]['token'], card)
|
|
end
|
|
|
|
it 'should properly add card for event' do
|
|
expect(supporter.cards.count).to eq(1)
|
|
stripe_customer = nil
|
|
expect(Stripe::Customer).to receive(:create).and_wrap_original { |m, *args| stripe_customer = m.call(*args); stripe_customer }
|
|
card_data = { holder_type: 'Supporter', holder_id: supporter.id, stripe_card_id: 'card_88888', stripe_card_token: stripe_card_token, name: 'card_name' }
|
|
orig_card = supporter.cards.first
|
|
card_ret = InsertCard.with_stripe(card_data, nil, event.id, user)
|
|
supporter.reload
|
|
card = supporter.cards.where('cards.name = ?', 'card_name').first
|
|
compare_card_returned_to_real(card_ret, card, card_ret[:json]['token'])
|
|
|
|
expected_card = {
|
|
id: card.id,
|
|
name: 'card_name',
|
|
stripe_card_token: stripe_card_token,
|
|
stripe_card_id: 'card_88888',
|
|
holder_id: supporter.id,
|
|
holder_type: 'Supporter',
|
|
stripe_customer_id: stripe_customer['id']
|
|
}.merge(default_card_attribs).with_indifferent_access
|
|
|
|
expect(card.attributes).to eq expected_card
|
|
|
|
expect(supporter.cards.count).to eq(2)
|
|
|
|
expect(Card.where('holder_id = ? and holder_type = ?', supporter.id, 'Supporter').count).to eq(2)
|
|
expect(Card.where('holder_id = ? and holder_type = ? and inactive != ?', supporter.id, 'Supporter', false).count).to eq(0)
|
|
|
|
expect(supporter.cards.find(orig_card.id)).to eq(orig_card)
|
|
|
|
verify_cust_added_supporter(card.stripe_customer_id, supporter.id)
|
|
|
|
verify_event_source_token(card_ret[:json]['token'], card, event)
|
|
end
|
|
end
|
|
|
|
context 'card doesnt exist' do
|
|
it 'invalid params get ignored' do
|
|
stripe_customer = nil
|
|
expect(Stripe::Customer).to receive(:create).and_wrap_original { |m, *args| stripe_customer = m.call(*args); stripe_customer }
|
|
card_data = { holder_type: 'Supporter', holder_id: supporter.id, stripe_card_id: 'card_88888', stripe_card_token: stripe_card_token,
|
|
name: 'card_name', created_at: DateTime.new(0), updated_at: DateTime.new(0), inactive: true }
|
|
|
|
card_ret = InsertCard.with_stripe(card_data)
|
|
|
|
card = Card.find(card_ret[:json][:id])
|
|
|
|
supporter.reload
|
|
|
|
compare_card_returned_to_real(card_ret, card, card_ret[:json]['token'])
|
|
|
|
expected_card = {
|
|
id: card.id,
|
|
holder_type: 'Supporter',
|
|
holder_id: supporter.id,
|
|
stripe_card_token: stripe_card_token,
|
|
name: 'card_name',
|
|
stripe_card_id: 'card_88888',
|
|
stripe_customer_id: stripe_customer['id']
|
|
}.merge(default_card_attribs).with_indifferent_access
|
|
|
|
expect(card.attributes).to eq expected_card
|
|
|
|
verify_supporter_source_token(card_ret[:json]['token'], card)
|
|
end
|
|
|
|
it 'should properly add supporter card when no card exist' do
|
|
stripe_customer = nil
|
|
expect(Stripe::Customer).to receive(:create).and_wrap_original { |m, *args| stripe_customer = m.call(*args); stripe_customer }
|
|
|
|
card_data = { holder_type: 'Supporter', holder_id: supporter.id, stripe_card_id: 'card_88888', stripe_card_token: stripe_card_token, name: 'card_name' }
|
|
card_ret = InsertCard.with_stripe(card_data)
|
|
supporter.reload
|
|
card = supporter.cards.where('cards.name = ?', 'card_name').first
|
|
compare_card_returned_to_real(card_ret, card, card_ret[:json]['token'])
|
|
expected_card = {
|
|
id: card.id,
|
|
name: 'card_name',
|
|
stripe_card_id: 'card_88888',
|
|
stripe_card_token: stripe_card_token,
|
|
stripe_customer_id: stripe_customer['id'],
|
|
holder_type: 'Supporter',
|
|
holder_id: supporter.id
|
|
}.merge(default_card_attribs).with_indifferent_access
|
|
|
|
expect(card.attributes).to eq expected_card
|
|
|
|
expect(supporter.cards.count).to eq(1)
|
|
|
|
expect(Card.where('holder_id = ? and holder_type = ?', supporter.id, 'Supporter').count).to eq(1)
|
|
expect(Card.where('holder_id = ? and holder_type = ? and inactive != ?', supporter.id, 'Supporter', false).count).to eq(0)
|
|
verify_cust_added_supporter(card.stripe_customer_id, supporter.id)
|
|
|
|
verify_supporter_source_token(card_ret[:json]['token'], card)
|
|
end
|
|
|
|
it 'should properly add card for event' do
|
|
stripe_customer = nil
|
|
expect(Stripe::Customer).to receive(:create).and_wrap_original { |m, *args| stripe_customer = m.call(*args); stripe_customer }
|
|
card_data = { holder_type: 'Supporter', holder_id: supporter.id, stripe_card_id: 'card_88888', stripe_card_token: stripe_card_token, name: 'card_name' }
|
|
card_ret = InsertCard.with_stripe(card_data, nil, event.id, user)
|
|
supporter.reload
|
|
card = supporter.cards.where('cards.name = ?', 'card_name').first
|
|
compare_card_returned_to_real(card_ret, card, card_ret[:json]['token'])
|
|
|
|
expected_card = {
|
|
id: card.id,
|
|
name: 'card_name',
|
|
stripe_card_token: stripe_card_token,
|
|
stripe_card_id: 'card_88888',
|
|
holder_id: supporter.id,
|
|
holder_type: 'Supporter',
|
|
stripe_customer_id: stripe_customer['id']
|
|
}.merge(default_card_attribs).with_indifferent_access
|
|
|
|
expect(card.attributes).to eq expected_card
|
|
|
|
expect(supporter.cards.count).to eq(1)
|
|
|
|
expect(Card.where('holder_id = ? and holder_type = ?', supporter.id, 'Supporter').count).to eq(1)
|
|
expect(Card.where('holder_id = ? and holder_type = ? and inactive != ?', supporter.id, 'Supporter', false).count).to eq(0)
|
|
|
|
verify_cust_added_supporter(card.stripe_customer_id, supporter.id)
|
|
|
|
verify_event_source_token(card_ret[:json]['token'], card, event)
|
|
end
|
|
end
|
|
|
|
it 'should return proper error when no supporter exists' do
|
|
ret = InsertCard.with_stripe(holder_id: 5_555_555, holder_type: 'Supporter', stripe_card_id: 'card_fafjeht', stripe_card_token: stripe_card_token, name: 'name')
|
|
expect(ret[:status]).to eq(:unprocessable_entity)
|
|
expect(ret[:json][:error]).to include('Sorry, you need to provide a nonprofit or supporter')
|
|
end
|
|
|
|
it 'should return proper error when you try to add using an event with unauthorized user' do
|
|
ret = InsertCard.with_stripe({ holder_id: supporter.id, holder_type: 'Supporter', stripe_card_id: 'card_fafjeht', stripe_card_token: stripe_card_token, name: 'name' }, nil, event.id, user_not_from_nonprofit)
|
|
|
|
expect(ret[:json][:error]).to eq "You're not authorized to perform that action"
|
|
expect(ret[:status]).to eq :unauthorized
|
|
end
|
|
|
|
it 'should return proper error when an invalid event_id is provided' do
|
|
ret = InsertCard.with_stripe({ holder_id: supporter.id, holder_type: 'Supporter', stripe_card_id: 'card_fafjeht', stripe_card_token: stripe_card_token, name: 'name' }, nil, 55_555, user_not_from_nonprofit)
|
|
expect(ret).to eq(status: :unprocessable_entity, json: { error: 'Oops! There was an error: 55555 is not a valid event' })
|
|
end
|
|
|
|
it 'should return proper error when event doesnt match the supporters nonprofit' do
|
|
supporter2 = force_create(:supporter, nonprofit: force_create(:fv_poverty))
|
|
ret = InsertCard.with_stripe({ holder_id: supporter2.id, holder_type: 'Supporter', stripe_card_id: 'card_fafjeht', stripe_card_token: stripe_card_token, name: 'name' }, nil, event.id, user_not_from_nonprofit)
|
|
expect(ret).to eq(status: :unprocessable_entity, json: { error: "Oops! There was an error: Event #{event.id} is not for the same nonprofit as supporter #{supporter2.id}" })
|
|
end
|
|
end
|
|
def compare_card_returned_to_real(card_ret, db_card, token = nil)
|
|
expect(card_ret[:status]).to eq(:ok)
|
|
|
|
expected_json = db_card.attributes
|
|
expected_json['token'] = token
|
|
expect(token).to match(UUID::Regex) if token
|
|
expect(card_ret[:json]).to eq(expected_json)
|
|
end
|
|
|
|
def verify_cust_added(stripe_customer_id, holder_id, holder_type)
|
|
customer = Stripe::Customer.retrieve(stripe_customer_id)
|
|
# does the customer exist? Was it set properly? Was the card set properly
|
|
expect(customer).to_not be_nil
|
|
expected_metadata = {
|
|
holder_id: holder_id,
|
|
holder_type: holder_type,
|
|
cardholders_name: nil
|
|
}
|
|
|
|
expect(customer.metadata.to_hash).to eq expected_metadata
|
|
customer
|
|
end
|
|
|
|
def verify_source_token(source_token, card, max_uses, expiration_time, event = nil)
|
|
tok = SourceToken.where('token = ?', source_token).first
|
|
expected = {
|
|
created_at: Time.now,
|
|
updated_at: Time.now,
|
|
tokenizable_id: card.id,
|
|
tokenizable_type: 'Card',
|
|
max_uses: max_uses,
|
|
total_uses: 0,
|
|
expiration: expiration_time,
|
|
event_id: event ? event.id : nil,
|
|
token: source_token
|
|
}.with_indifferent_access
|
|
|
|
expect(tok.attributes).to eq expected
|
|
end
|
|
end
|
|
end
|