Creates StripeDispute and its respective event publishing
This commit is contained in:
parent
dbcfa38696
commit
1c9aecd605
8 changed files with 433 additions and 4 deletions
|
@ -175,6 +175,18 @@ class ObjectEventListener < ApplicationListener
|
|||
enqueue_transmissions_to_webhooks(event)
|
||||
end
|
||||
|
||||
def self.stripe_transaction_dispute_created(event)
|
||||
enqueue_transmissions_to_webhooks(event)
|
||||
end
|
||||
|
||||
def self.stripe_transaction_dispute_updated(event)
|
||||
enqueue_transmissions_to_webhooks(event)
|
||||
end
|
||||
|
||||
def self.stripe_transaction_dispute_deleted(event)
|
||||
enqueue_transmissions_to_webhooks(event)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.enqueue_transmissions_to_webhooks(event)
|
||||
|
|
107
app/models/stripe_dispute.rb
Normal file
107
app/models/stripe_dispute.rb
Normal file
|
@ -0,0 +1,107 @@
|
|||
# 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
|
||||
class StripeDispute < ApplicationRecord
|
||||
include Model::SubtransactionPaymentable
|
||||
belongs_to :payment
|
||||
|
||||
delegate :gross_amount, :net_amount, :fee_total, to: :payment
|
||||
|
||||
delegate :currency, to: :nonprofit
|
||||
|
||||
def stripe_id
|
||||
payment.dispute.stripe_dispute_id
|
||||
end
|
||||
|
||||
concerning :JBuilder do # rubocop:disable Metrics/BlockLength
|
||||
included do
|
||||
setup_houid :stripedisp
|
||||
end
|
||||
|
||||
def to_builder(*expand) # rubocop:disable Metrics/AbcSize
|
||||
init_builder(*expand) do |json|
|
||||
json.object 'stripe_transaction_dispute'
|
||||
json.gross_amount do
|
||||
json.cents gross_amount
|
||||
json.currency currency
|
||||
end
|
||||
|
||||
json.net_amount do
|
||||
json.cents net_amount
|
||||
json.currency currency
|
||||
end
|
||||
|
||||
json.fee_total do
|
||||
json.cents fee_total
|
||||
json.currency currency
|
||||
end
|
||||
|
||||
json.created payment.date.to_i
|
||||
|
||||
json.stripe_id stripe_id
|
||||
|
||||
json.type 'payment'
|
||||
|
||||
json.add_builder_expansion :nonprofit, :supporter, :subtransaction
|
||||
|
||||
json.add_builder_expansion :trx, json_attribute: :transaction
|
||||
end
|
||||
end
|
||||
|
||||
def to_id
|
||||
::Jbuilder.new do |json|
|
||||
json.(self, :id)
|
||||
json.object 'stripe_transaction_dispute'
|
||||
json.type 'payment'
|
||||
end
|
||||
end
|
||||
|
||||
def publish_created
|
||||
Houdini.event_publisher.announce(
|
||||
:stripe_transaction_dispute_created,
|
||||
to_event('stripe_transaction_dispute.created', :nonprofit, :trx, :supporter, :subtransaction)
|
||||
.attributes!
|
||||
)
|
||||
Houdini.event_publisher.announce(
|
||||
:payment_created,
|
||||
to_event('payment.created', :nonprofit, :trx, :supporter, :subtransaction)
|
||||
.attributes!
|
||||
)
|
||||
end
|
||||
|
||||
def publish_updated
|
||||
Houdini.event_publisher.announce(
|
||||
:stripe_transaction_dispute_updated,
|
||||
to_event('stripe_transaction_dispute.updated', :nonprofit, :trx, :supporter, :subtransaction)
|
||||
.attributes!
|
||||
)
|
||||
Houdini.event_publisher.announce(
|
||||
:payment_updated,
|
||||
to_event('payment.updated', :nonprofit, :trx, :supporter, :subtransaction)
|
||||
.attributes!
|
||||
)
|
||||
end
|
||||
|
||||
def publish_deleted
|
||||
Houdini.event_publisher.announce(
|
||||
:stripe_transaction_dispute_deleted,
|
||||
to_event('stripe_transaction_dispute.deleted',
|
||||
:nonprofit,
|
||||
:trx,
|
||||
:supporter,
|
||||
:subtransaction).attributes!
|
||||
)
|
||||
Houdini.event_publisher.announce(
|
||||
:payment_deleted,
|
||||
to_event(
|
||||
'payment.deleted',
|
||||
:nonprofit,
|
||||
:trx,
|
||||
:supporter,
|
||||
:subtransaction
|
||||
).attributes!
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -13,8 +13,13 @@ class SubtransactionPayment < ApplicationRecord
|
|||
has_one :supporter, through: :subtransaction
|
||||
has_one :nonprofit, through: :subtransaction
|
||||
|
||||
delegated_type :paymentable,
|
||||
types: %w[OfflineTransactionCharge StripeCharge OfflineTransactionRefund OfflineTransactionDispute]
|
||||
delegated_type :paymentable, types: %w[
|
||||
OfflineTransactionCharge
|
||||
OfflineTransactionDispute
|
||||
OfflineTransactionRefund
|
||||
StripeCharge
|
||||
StripeDispute
|
||||
]
|
||||
|
||||
delegate :gross_amount, :fee_total, :net_amount, to: :paymentable
|
||||
|
||||
|
|
9
db/migrate/20210621191202_create_stripe_disputes.rb
Normal file
9
db/migrate/20210621191202_create_stripe_disputes.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
class CreateStripeDisputes < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
create_table :stripe_disputes, id: :string do |t|
|
||||
t.references :payment, foreign_key: true, id: :string
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
18
db/schema.rb
18
db/schema.rb
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2021_06_02_220525) do
|
||||
ActiveRecord::Schema.define(version: 2021_06_21_191202) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_stat_statements"
|
||||
|
@ -814,6 +814,20 @@ ActiveRecord::Schema.define(version: 2021_06_02_220525) do
|
|||
t.index ["payment_id"], name: "index_stripe_charges_on_payment_id"
|
||||
end
|
||||
|
||||
create_table "stripe_disputes", id: :string, force: :cascade do |t|
|
||||
t.bigint "payment_id"
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.index ["payment_id"], name: "index_stripe_disputes_on_payment_id"
|
||||
end
|
||||
|
||||
create_table "stripe_refunds", id: :string, force: :cascade do |t|
|
||||
t.bigint "payment_id"
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.index ["payment_id"], name: "index_stripe_refunds_on_payment_id"
|
||||
end
|
||||
|
||||
create_table "stripe_transactions", id: :string, force: :cascade do |t|
|
||||
t.integer "amount", null: false
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
|
@ -1051,6 +1065,8 @@ ActiveRecord::Schema.define(version: 2021_06_02_220525) do
|
|||
add_foreign_key "recurrences", "recurring_donations"
|
||||
add_foreign_key "recurrences", "supporters"
|
||||
add_foreign_key "stripe_charges", "payments"
|
||||
add_foreign_key "stripe_disputes", "payments"
|
||||
add_foreign_key "stripe_refunds", "payments"
|
||||
add_foreign_key "subtransaction_payments", "subtransactions"
|
||||
add_foreign_key "subtransactions", "transactions"
|
||||
add_foreign_key "ticket_purchases", "event_discounts"
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
# Full license explanation at https://github.com/houdiniproject/houdini/blob/master/LICENSE
|
||||
FactoryBot.define do
|
||||
factory :stripe_charge do
|
||||
references { '' }
|
||||
payment { '' }
|
||||
end
|
||||
end
|
||||
|
|
9
spec/factories/stripe_disputes.rb
Normal file
9
spec/factories/stripe_disputes.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# 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
|
||||
FactoryBot.define do
|
||||
factory :stripe_dispute do
|
||||
payment { '' }
|
||||
end
|
||||
end
|
271
spec/models/stripe_dispute_spec.rb
Normal file
271
spec/models/stripe_dispute_spec.rb
Normal file
|
@ -0,0 +1,271 @@
|
|||
# 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'
|
||||
|
||||
RSpec.describe StripeDispute, type: :model do
|
||||
let!(:nonprofit) { create(:nm_justice) }
|
||||
let!(:supporter) { create(:supporter, nonprofit: nonprofit) }
|
||||
let!(:payment) do
|
||||
force_create(
|
||||
:payment,
|
||||
gross_amount: 500,
|
||||
net_amount: 400,
|
||||
fee_total: 100,
|
||||
date: Time.zone.now,
|
||||
nonprofit: nonprofit,
|
||||
supporter: supporter
|
||||
)
|
||||
end
|
||||
|
||||
let(:stripe_transaction_charge) do
|
||||
build(
|
||||
:stripe_charge,
|
||||
payment:
|
||||
force_create(
|
||||
:payment,
|
||||
gross_amount: 500,
|
||||
net_amount: 400,
|
||||
fee_total: 100,
|
||||
date: Time.zone.now,
|
||||
nonprofit: nonprofit,
|
||||
supporter: supporter
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
let!(:stripe_transaction_dispute) do
|
||||
build(
|
||||
:stripe_dispute,
|
||||
payment: payment
|
||||
)
|
||||
end
|
||||
|
||||
let(:transaction) do
|
||||
trx = supporter.transactions.build(amount: 500)
|
||||
trx.build_subtransaction(
|
||||
subtransactable: StripeTransaction.new(amount: 500),
|
||||
subtransaction_payments: [
|
||||
build(:subtransaction_payment, paymentable: stripe_transaction_charge),
|
||||
build(:subtransaction_payment, paymentable: stripe_transaction_dispute)
|
||||
]
|
||||
)
|
||||
trx.save!
|
||||
trx
|
||||
end
|
||||
|
||||
let(:event_publisher) { double }
|
||||
|
||||
let(:expected_event) do
|
||||
{
|
||||
'data' => {
|
||||
'object' => {
|
||||
'created' => kind_of(Numeric),
|
||||
'fee_total' => { 'cents' => 100, 'currency' => nonprofit.currency },
|
||||
'gross_amount' => { 'cents' => 500, 'currency' => nonprofit.currency },
|
||||
'id' => match_houid('stripedisp'),
|
||||
'stripe_id' => kind_of(String),
|
||||
'net_amount' => { 'cents' => 400, 'currency' => nonprofit.currency },
|
||||
'nonprofit' => {
|
||||
'id' => nonprofit.id,
|
||||
'name' => nonprofit.name,
|
||||
'object' => 'nonprofit'
|
||||
},
|
||||
'object' => 'stripe_transaction_dispute',
|
||||
'subtransaction' => {
|
||||
'created' => kind_of(Numeric),
|
||||
'id' => match_houid('stripetrx'),
|
||||
'initial_amount' => { 'cents' => 500, 'currency' => nonprofit.currency },
|
||||
'net_amount' => { 'cents' => 800, 'currency' => nonprofit.currency },
|
||||
'nonprofit' => nonprofit.id,
|
||||
'object' => 'stripe_transaction',
|
||||
'payments' => [
|
||||
{
|
||||
'id' => match_houid('stripechrg'),
|
||||
'object' => 'stripe_transaction_charge',
|
||||
'type' => 'payment'
|
||||
},
|
||||
{
|
||||
'id' => match_houid('stripedisp'),
|
||||
'object' => 'stripe_transaction_dispute',
|
||||
'type' => 'payment'
|
||||
}
|
||||
],
|
||||
'supporter' => supporter.id,
|
||||
'transaction' => match_houid('trx'),
|
||||
'type' => 'subtransaction'
|
||||
},
|
||||
'supporter' => {
|
||||
'anonymous' => supporter.anonymous,
|
||||
'deleted' => supporter.deleted,
|
||||
'id' => supporter.id,
|
||||
'merged_into' => supporter.merged_into,
|
||||
'name' => supporter.name,
|
||||
'nonprofit' => nonprofit.id,
|
||||
'object' => 'supporter',
|
||||
'organization' => supporter.organization,
|
||||
'phone' => supporter.phone,
|
||||
'supporter_addresses' => [kind_of(Numeric)]
|
||||
},
|
||||
'transaction' => {
|
||||
'amount' => { 'cents' => 500, 'currency' => nonprofit.currency },
|
||||
'created' => kind_of(Numeric),
|
||||
'id' => match_houid('trx'),
|
||||
'nonprofit' => nonprofit.id,
|
||||
'object' => 'transaction',
|
||||
'subtransaction' => {
|
||||
'id' => match_houid('stripetrx'),
|
||||
'object' => 'stripe_transaction',
|
||||
'type' => 'subtransaction'
|
||||
},
|
||||
'subtransaction_payments' => [
|
||||
{
|
||||
'id' => match_houid('stripechrg'),
|
||||
'object' => 'stripe_transaction_charge',
|
||||
'type' => 'payment'
|
||||
}, {
|
||||
'id' => match_houid('stripedisp'),
|
||||
'object' => 'stripe_transaction_dispute',
|
||||
'type' => 'payment'
|
||||
}
|
||||
],
|
||||
'supporter' => supporter.id,
|
||||
'transaction_assignments' => []
|
||||
},
|
||||
'type' => 'payment'
|
||||
}
|
||||
},
|
||||
'id' => match_houid('objevt'),
|
||||
'object' => 'object_event',
|
||||
'type' => 'event_type'
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
allow(Houdini)
|
||||
.to receive(:event_publisher)
|
||||
.and_return(event_publisher)
|
||||
force_create(:dispute, payment: payment, stripe_dispute_id: 'some_id')
|
||||
transaction
|
||||
end
|
||||
|
||||
describe 'stripe transaction dispute' do
|
||||
subject { stripe_transaction_dispute }
|
||||
|
||||
it do
|
||||
is_expected
|
||||
.to have_attributes(
|
||||
nonprofit: an_instance_of(Nonprofit),
|
||||
id: match_houid('stripedisp')
|
||||
)
|
||||
end
|
||||
|
||||
it { is_expected.to be_persisted }
|
||||
end
|
||||
|
||||
describe '.to_builder' do
|
||||
subject { JSON.parse(stripe_transaction_dispute.to_builder.target!) }
|
||||
|
||||
it do
|
||||
is_expected
|
||||
.to match_json(
|
||||
{
|
||||
object: 'stripe_transaction_dispute',
|
||||
nonprofit: nonprofit.id,
|
||||
supporter: supporter.id,
|
||||
id: match_houid('stripedisp'),
|
||||
stripe_id: kind_of(String),
|
||||
type: 'payment',
|
||||
fee_total: { cents: 100, currency: nonprofit.currency },
|
||||
net_amount: { cents: 400, currency: nonprofit.currency },
|
||||
gross_amount: { cents: 500, currency: nonprofit.currency },
|
||||
created: kind_of(Numeric),
|
||||
subtransaction: { id: match_houid('stripetrx'), object: 'stripe_transaction', type: 'subtransaction' },
|
||||
transaction: match_houid('trx')
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.publish_created' do
|
||||
before do
|
||||
expected_event['type'] = 'stripe_transaction_dispute.created'
|
||||
|
||||
allow(event_publisher)
|
||||
.to receive(:announce)
|
||||
.with(:payment_created, anything)
|
||||
allow(event_publisher)
|
||||
.to receive(:announce)
|
||||
.with(
|
||||
:stripe_transaction_dispute_created,
|
||||
expected_event
|
||||
)
|
||||
end
|
||||
|
||||
it 'announces stripe_transaction_dispute.created event' do
|
||||
stripe_transaction_dispute.publish_created
|
||||
|
||||
expect(event_publisher)
|
||||
.to have_received(:announce)
|
||||
.with(
|
||||
:stripe_transaction_dispute_created,
|
||||
expected_event
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.publish_updated' do
|
||||
before do
|
||||
expected_event['type'] = 'stripe_transaction_dispute.updated'
|
||||
|
||||
allow(event_publisher)
|
||||
.to receive(:announce)
|
||||
.with(:payment_updated, anything)
|
||||
allow(event_publisher)
|
||||
.to receive(:announce)
|
||||
.with(
|
||||
:stripe_transaction_dispute_updated,
|
||||
expected_event
|
||||
)
|
||||
end
|
||||
|
||||
it 'announces stripe_transaction_dispute.updated event' do
|
||||
stripe_transaction_dispute.publish_updated
|
||||
|
||||
expect(event_publisher)
|
||||
.to have_received(:announce)
|
||||
.with(
|
||||
:stripe_transaction_dispute_updated,
|
||||
expected_event
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.publish_deleted' do
|
||||
before do
|
||||
expected_event['type'] = 'stripe_transaction_dispute.deleted'
|
||||
|
||||
allow(event_publisher)
|
||||
.to receive(:announce)
|
||||
.with(:payment_deleted, anything)
|
||||
allow(event_publisher)
|
||||
.to receive(:announce)
|
||||
.with(
|
||||
:stripe_transaction_dispute_deleted,
|
||||
expected_event
|
||||
)
|
||||
end
|
||||
|
||||
it 'announces stripe_transaction_dispute.deleted event' do
|
||||
stripe_transaction_dispute.publish_deleted
|
||||
|
||||
expect(event_publisher)
|
||||
.to have_received(:announce)
|
||||
.with(
|
||||
:stripe_transaction_dispute_deleted,
|
||||
expected_event
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue