mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-27 21:06:49 +00:00
This spec was using a very old syntax no longer supported by RSpec. It's not currently influencing specs result because the spec running into the error is currently set as "pending". However, the spec is still run and the error is still visible. Fixing the syntax does not fix the spec, but lets it get a bit further.
1068 lines
37 KiB
Ruby
1068 lines
37 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'spec_helper'
|
|
|
|
RSpec.describe Spree::Payment do
|
|
context 'original specs from Spree' do
|
|
before { Stripe.api_key = "sk_test_12345" }
|
|
let(:order) { create(:order) }
|
|
let(:shop) { create(:enterprise) }
|
|
let(:payment_method) {
|
|
payment_method = create(:stripe_sca_payment_method,
|
|
distributor_ids: [create(:distributor_enterprise).id],
|
|
preferred_enterprise_id: shop.id)
|
|
allow(payment_method).to receive(:source_required) { true }
|
|
allow(payment_method).to receive(:payment) { true }
|
|
payment_method
|
|
}
|
|
|
|
let(:card) {
|
|
create(:credit_card)
|
|
}
|
|
|
|
let(:payment) do
|
|
payment = create(:payment)
|
|
payment.source = card
|
|
payment.order = order
|
|
payment.payment_method = payment_method
|
|
payment.response_code = "12345"
|
|
payment
|
|
end
|
|
|
|
let(:amount_in_cents) { payment.amount.to_f * 100 }
|
|
|
|
let(:success_response) do
|
|
instance_double(
|
|
ActiveMerchant::Billing::Response,
|
|
authorization: "123",
|
|
success?: true,
|
|
avs_result: { 'code' => 'avs-code' },
|
|
cvv_result: { code: nil, message: nil }
|
|
)
|
|
end
|
|
let(:failed_response) { instance_double(ActiveMerchant::Billing::Response, success?: false) }
|
|
|
|
context "extends LocalizedNumber" do
|
|
subject { build_stubbed(:payment) }
|
|
it_behaves_like "a model using the LocalizedNumber module", [:amount]
|
|
end
|
|
|
|
context 'validations' do
|
|
it "returns useful error messages when source is invalid" do
|
|
payment = build_stubbed(:payment, source: Spree::CreditCard.new)
|
|
expect(payment).not_to be_valid
|
|
cc_errors = payment.errors['Credit Card']
|
|
expect(cc_errors).to include("Number can't be blank")
|
|
expect(cc_errors).to include("Month is not a number")
|
|
expect(cc_errors).to include("Year is not a number")
|
|
expect(cc_errors).to include("Verification Value can't be blank")
|
|
end
|
|
end
|
|
|
|
context "creating a new payment alongside other incomplete payments" do
|
|
let(:order) { create(:order_with_totals) }
|
|
let!(:incomplete_payment) { create(:payment, order:, state: "pending") }
|
|
let(:new_payment) { create(:payment, order:, state: "checkout") }
|
|
|
|
it "invalidates other incomplete payments on the order" do
|
|
new_payment
|
|
|
|
expect(incomplete_payment.reload.state).to eq "invalid"
|
|
end
|
|
end
|
|
|
|
# Regression test for https://github.com/spree/spree/pull/2224
|
|
context 'failure' do
|
|
it 'should transition to failed from pending state' do
|
|
payment.state = 'pending'
|
|
payment.failure
|
|
expect(payment.state).to eql('failed')
|
|
end
|
|
|
|
it 'should transition to failed from processing state' do
|
|
payment.state = 'processing'
|
|
payment.failure
|
|
expect(payment.state).to eql('failed')
|
|
end
|
|
end
|
|
|
|
context 'invalidate' do
|
|
it 'should transition from checkout to invalid' do
|
|
payment.state = 'checkout'
|
|
payment.invalidate
|
|
expect(payment.state).to eq('invalid')
|
|
end
|
|
end
|
|
|
|
context "processing" do
|
|
before do
|
|
allow(payment).to receive(:record_response)
|
|
allow(card).to receive(:has_payment_profile?).and_return(true)
|
|
allow(payment).to receive(:update_order).and_return(true)
|
|
allow(payment).to receive(:create_payment_profile).and_return(true)
|
|
end
|
|
|
|
context "#process!" do
|
|
it "should call purchase!" do
|
|
payment = build_stubbed(:payment, payment_method:)
|
|
expect(payment).to receive(:purchase!)
|
|
payment.process!
|
|
end
|
|
|
|
it "should make the state 'processing'" do
|
|
allow(payment_method).to receive(:capture).and_return(success_response)
|
|
allow(payment_method).to receive(:purchase).and_return(success_response)
|
|
|
|
expect(payment).to receive(:started_processing!)
|
|
|
|
payment.save!
|
|
payment.process!
|
|
end
|
|
|
|
it "should invalidate if payment method doesnt support source" do
|
|
expect(payment.payment_method).to receive(:supports?)
|
|
.with(payment.source).and_return(false)
|
|
expect { payment.process! }.to raise_error(Spree::Core::GatewayError)
|
|
expect(payment.state).to eq('invalid')
|
|
end
|
|
|
|
context "the payment is already authorized" do
|
|
before do
|
|
allow(payment).to receive(:response_code) { "pi_123" }
|
|
end
|
|
|
|
it "should call purchase" do
|
|
expect(payment).to receive(:purchase!)
|
|
payment.process!
|
|
end
|
|
end
|
|
end
|
|
|
|
context "#process_offline when payment is already authorized" do
|
|
before do
|
|
allow(payment).to receive(:response_code) { "pi_123" }
|
|
end
|
|
|
|
it "should call capture if the payment is already authorized" do
|
|
expect(payment).to receive(:capture!)
|
|
expect(payment).not_to receive(:purchase!)
|
|
payment.process_offline!
|
|
end
|
|
end
|
|
|
|
context "#authorize" do
|
|
before do
|
|
allow(payment_method).to receive(:authorize) { success_response }
|
|
end
|
|
|
|
it "should call authorize on the gateway with the payment amount" do
|
|
expect(payment.payment_method).to receive(:authorize).with(
|
|
amount_in_cents, card, anything
|
|
).and_return(success_response)
|
|
payment.authorize!
|
|
end
|
|
|
|
it "should call authorize on the gateway with the currency code" do
|
|
allow(payment).to receive(:currency) { "GBP" }
|
|
expect(payment.payment_method).to receive(:authorize).with(
|
|
amount_in_cents, card, hash_including({ currency: "GBP" })
|
|
).and_return(success_response)
|
|
payment.authorize!
|
|
end
|
|
|
|
it "should log the response" do
|
|
payment.authorize!
|
|
expect(payment).to have_received(:record_response)
|
|
end
|
|
|
|
context "when gateway does not match the environment" do
|
|
it "should raise an exception" do
|
|
allow(payment_method).to receive(:environment) { "foo" }
|
|
expect { payment.authorize! }.to raise_error(Spree::Core::GatewayError)
|
|
end
|
|
end
|
|
|
|
context "if successful" do
|
|
before do
|
|
expect(payment.payment_method).to receive(:authorize).with(
|
|
amount_in_cents, card, anything
|
|
).and_return(success_response)
|
|
end
|
|
|
|
it "should store the response_code, avs_response and cvv_response fields" do
|
|
payment.authorize!
|
|
expect(payment.response_code).to eq('123')
|
|
expect(payment.avs_response).to eq('avs-code')
|
|
end
|
|
|
|
it "should make payment pending" do
|
|
expect(payment).to receive(:pend!)
|
|
payment.authorize!
|
|
end
|
|
end
|
|
|
|
context "authorization is required" do
|
|
before do
|
|
allow(success_response).to receive(:cvv_result) {
|
|
{ 'code' => nil,
|
|
'message' => nil,
|
|
'redirect_auth_url' => "https://stripe.com/redirect" }
|
|
}
|
|
expect(payment.payment_method).to receive(:authorize).with(
|
|
amount_in_cents, card, anything
|
|
).and_return(success_response)
|
|
end
|
|
|
|
it "should move the payment to requires_authorization" do
|
|
expect(payment).to receive(:require_authorization!)
|
|
payment.authorize!
|
|
end
|
|
end
|
|
|
|
context "if unsuccessful" do
|
|
it "should mark payment as failed" do
|
|
allow(payment_method).to receive(:authorize).and_return(failed_response)
|
|
expect(payment).to receive(:failure)
|
|
expect(payment).not_to receive(:pend)
|
|
expect {
|
|
payment.authorize!
|
|
}.to raise_error(Spree::Core::GatewayError)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "purchase" do
|
|
before do
|
|
allow(payment_method).to receive(:purchase).and_return(success_response)
|
|
end
|
|
|
|
it "should call purchase on the gateway with the payment amount" do
|
|
expect(payment_method).to receive(:purchase).with(amount_in_cents, card,
|
|
anything).and_return(success_response)
|
|
payment.purchase!
|
|
end
|
|
|
|
it "should log the response" do
|
|
payment.purchase!
|
|
expect(payment).to have_received(:record_response)
|
|
end
|
|
|
|
context "when gateway does not match the environment" do
|
|
it "should raise an exception" do
|
|
allow(payment_method).to receive(:environment) { "foo" }
|
|
expect { payment.purchase! }.to raise_error(Spree::Core::GatewayError)
|
|
end
|
|
end
|
|
|
|
context "if successful" do
|
|
before do
|
|
expect(payment.payment_method).to receive(:purchase).with(
|
|
amount_in_cents, card, anything
|
|
).and_return(success_response)
|
|
end
|
|
|
|
it "should store the response_code and avs_response" do
|
|
payment.purchase!
|
|
expect(payment.response_code).to eq('123')
|
|
expect(payment.avs_response).to eq('avs-code')
|
|
end
|
|
|
|
it "should make payment complete" do
|
|
expect(payment).to receive(:complete!)
|
|
payment.purchase!
|
|
end
|
|
end
|
|
|
|
context "if unsuccessful" do
|
|
it "should make payment failed" do
|
|
allow(payment_method).to receive(:purchase).and_return(failed_response)
|
|
expect(payment).to receive(:failure)
|
|
expect(payment).not_to receive(:pend)
|
|
expect { payment.purchase! }.to raise_error(Spree::Core::GatewayError)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "#capture" do
|
|
before do
|
|
allow(payment).to receive(:complete).and_return(true)
|
|
end
|
|
|
|
context "when payment is pending" do
|
|
before do
|
|
payment.state = 'pending'
|
|
end
|
|
|
|
context "if successful" do
|
|
before do
|
|
expect(payment.payment_method).to receive(:capture).and_return(success_response)
|
|
end
|
|
|
|
it "should make payment complete" do
|
|
expect(payment).to receive(:complete)
|
|
payment.capture!
|
|
end
|
|
|
|
it "should store the response_code" do
|
|
allow(payment_method).to receive(:capture).and_return(success_response)
|
|
payment.capture!
|
|
expect(payment.response_code).to eq('123')
|
|
end
|
|
end
|
|
|
|
context "if unsuccessful" do
|
|
it "should not make payment complete" do
|
|
allow(payment_method).to receive(:capture).and_return(failed_response)
|
|
expect(payment).to receive(:failure)
|
|
expect(payment).not_to receive(:complete)
|
|
expect { payment.capture! }.to raise_error(Spree::Core::GatewayError)
|
|
end
|
|
end
|
|
end
|
|
|
|
# Regression test for #2119
|
|
context "when payment is completed" do
|
|
it "should do nothing" do
|
|
payment = build_stubbed(:payment, :completed)
|
|
expect(payment).not_to receive(:complete)
|
|
expect(payment.payment_method).not_to receive(:capture)
|
|
expect(payment.log_entries).not_to receive(:create)
|
|
payment.capture!
|
|
end
|
|
end
|
|
end
|
|
|
|
context "#void" do
|
|
before do
|
|
payment.response_code = '123'
|
|
payment.state = 'pending'
|
|
|
|
allow(payment_method).to receive(:void).and_return(success_response)
|
|
end
|
|
|
|
context "when profiles are supported" do
|
|
it "should call payment_enterprise.void with the payment's response_code" do
|
|
allow(payment_method).to receive(:payment_profiles_supported) { true }
|
|
expect(payment_method).to receive(:void).with('123', card,
|
|
anything).and_return(success_response)
|
|
payment.void_transaction!
|
|
end
|
|
end
|
|
|
|
context "when profiles are not supported" do
|
|
it "should call payment_gateway.void with the payment's response_code" do
|
|
allow(payment_method).to receive(:payment_profiles_supported) { false }
|
|
expect(payment_method).to receive(:void).with('123', card,
|
|
anything).and_return(success_response)
|
|
payment.void_transaction!
|
|
end
|
|
end
|
|
|
|
it "should log the response" do
|
|
payment.void_transaction!
|
|
expect(payment).to have_received(:record_response)
|
|
end
|
|
|
|
context "when payment_method does not match the environment" do
|
|
it "should raise an exception" do
|
|
payment = build_stubbed(:payment, payment_method:)
|
|
allow(payment_method).to receive(:environment) { "foo" }
|
|
expect { payment.void_transaction! }.to raise_error(Spree::Core::GatewayError)
|
|
end
|
|
end
|
|
|
|
context "if successful" do
|
|
it "should update the response_code with the authorization from the gateway" do
|
|
payment.response_code = 'abc'
|
|
payment.void_transaction!
|
|
|
|
expect(payment.response_code).to eq('123')
|
|
end
|
|
end
|
|
|
|
context "if unsuccessful" do
|
|
it "should not void the payment" do
|
|
allow(payment_method).to receive(:void).and_return(failed_response)
|
|
expect(payment).not_to receive(:void)
|
|
expect { payment.void_transaction! }.to raise_error(Spree::Core::GatewayError)
|
|
end
|
|
end
|
|
|
|
# Regression test for #2119
|
|
context "if payment is already voided" do
|
|
it "should not void the payment" do
|
|
payment = build_stubbed(:payment, payment_method:, state: 'void')
|
|
expect(payment.payment_method).not_to receive(:void)
|
|
payment.void_transaction!
|
|
end
|
|
end
|
|
|
|
context "if payment has any adjustment" do
|
|
let!(:order) { create(:order) }
|
|
let!(:payment_method) {
|
|
create(:payment_method, calculator: Calculator::FlatRate.new(preferred_amount: 10))
|
|
}
|
|
|
|
it "should create another adjustment and revoke the previous one" do
|
|
payment = create(:payment, order:, payment_method:)
|
|
expect(order.all_adjustments.payment_fee.eligible.length).to eq(1)
|
|
|
|
payment.void_transaction!
|
|
expect(order.all_adjustments.payment_fee.eligible.length).to eq(0)
|
|
|
|
payment = create(:payment, order:, payment_method:)
|
|
expect(order.all_adjustments.payment_fee.eligible.length).to eq(1)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#credit" do
|
|
before do
|
|
payment.state = 'completed'
|
|
payment.response_code = '123'
|
|
|
|
# Stub payment.method#credit
|
|
allow(payment_method).to receive(:credit).and_return(success_response)
|
|
end
|
|
|
|
context "when outstanding_balance is less than payment amount" do
|
|
before do
|
|
allow(payment.order).to receive(:outstanding_balance) { 10 }
|
|
allow(payment).to receive(:credit_allowed) { 1000 }
|
|
end
|
|
|
|
it "should call credit on the gateway with the credit amount and response_code" do
|
|
expect(payment_method).to receive(:credit).with(1000, card, '123',
|
|
anything).and_return(success_response)
|
|
payment.credit!
|
|
end
|
|
end
|
|
|
|
context "if payment method has any payment fees" do
|
|
before do
|
|
expect(payment.order).to receive(:outstanding_balance).at_least(:once) { 10 }
|
|
expect(payment).to receive(:credit_allowed) { 200 }
|
|
end
|
|
|
|
it "should not applied any transaction fees" do
|
|
payment.credit!
|
|
expect(payment.adjustment.finalized?).to eq(false)
|
|
expect(order.all_adjustments.payment_fee.length).to eq(0)
|
|
end
|
|
end
|
|
|
|
context "when outstanding_balance is equal to payment amount" do
|
|
before do
|
|
allow(payment.order).to receive(:outstanding_balance) { payment.amount }
|
|
end
|
|
|
|
it "should call credit on the gateway with the credit amount and response_code" do
|
|
expect(payment_method).to receive(:credit).with(
|
|
amount_in_cents, card, '123', anything
|
|
).and_return(success_response)
|
|
payment.credit!
|
|
end
|
|
end
|
|
|
|
context "when outstanding_balance is greater than payment amount" do
|
|
before do
|
|
allow(payment.order).to receive(:outstanding_balance) { 101 }
|
|
end
|
|
|
|
it "should call credit on the gateway with original payment amount and response_code" do
|
|
expect(payment_method).to receive(:credit).with(
|
|
amount_in_cents.to_f, card, '123', anything
|
|
).and_return(success_response)
|
|
payment.credit!
|
|
end
|
|
end
|
|
|
|
context "when amount <= credit_allowed" do
|
|
it "makes the state processing" do
|
|
payment.payment_method.name = 'Gateway'
|
|
payment.payment_method.distributors << create(:distributor_enterprise)
|
|
payment.payment_method.save!
|
|
|
|
payment.order = create(:order)
|
|
|
|
payment.state = 'completed'
|
|
allow(payment).to receive(:credit_allowed) { 10 }
|
|
payment.partial_credit(10)
|
|
expect(payment).to be_processing
|
|
end
|
|
|
|
it "calls credit on the source with the payment and amount" do
|
|
payment.state = 'completed'
|
|
allow(payment).to receive(:credit_allowed) { 10 }
|
|
expect(payment).to receive(:credit!).with(10)
|
|
payment.partial_credit(10)
|
|
end
|
|
end
|
|
|
|
context "when amount > credit_allowed" do
|
|
it "should not call credit on the source" do
|
|
payment = build_stubbed(:payment)
|
|
payment.state = 'completed'
|
|
allow(payment).to receive(:credit_allowed) { 10 }
|
|
payment.partial_credit(20)
|
|
expect(payment).to be_completed
|
|
end
|
|
end
|
|
|
|
it "should log the response" do
|
|
payment.credit!
|
|
expect(payment).to have_received(:record_response)
|
|
end
|
|
|
|
context "when gateway does not match the environment" do
|
|
it "should raise an exception" do
|
|
payment = build_stubbed(:payment, payment_method:)
|
|
allow(payment_method).to receive(:environment) { "foo" }
|
|
expect { payment.credit! }.to raise_error(Spree::Core::GatewayError)
|
|
end
|
|
end
|
|
|
|
context "when response is successful" do
|
|
it "should create an offsetting payment" do
|
|
expect(Spree::Payment).to receive(:create!)
|
|
payment.credit!
|
|
end
|
|
|
|
it "resulting payment should have correct values" do
|
|
allow(payment.order).to receive(:new_outstanding_balance) { 100 }
|
|
allow(payment).to receive(:credit_allowed) { 10 }
|
|
|
|
offsetting_payment = payment.credit!
|
|
expect(offsetting_payment.amount.to_f).to eq(-10)
|
|
expect(offsetting_payment).to be_completed
|
|
expect(offsetting_payment.response_code).to eq('123')
|
|
expect(offsetting_payment.source).to eq(payment)
|
|
end
|
|
|
|
context 'and the source payment card is expired' do
|
|
let(:card) do
|
|
Spree::CreditCard.new(month: 12, year: 1995, number: '4111111111111111',
|
|
verification_value: '123')
|
|
end
|
|
|
|
it 'lets the new payment to be saved' do
|
|
allow(payment.order).to receive(:new_outstanding_balance) { 100 }
|
|
allow(payment).to receive(:credit_allowed) { 10 }
|
|
|
|
offsetting_payment = payment.credit!
|
|
|
|
expect(offsetting_payment).to be_valid
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when response is unsuccessful" do
|
|
it "should not create a payment" do
|
|
allow(payment_method).to receive(:credit).and_return(failed_response)
|
|
expect(Spree::Payment).not_to receive(:create)
|
|
expect { payment.credit! }.to raise_error(Spree::Core::GatewayError)
|
|
end
|
|
end
|
|
|
|
context "when already processing" do
|
|
it "should return nil without trying to process the source" do
|
|
payment = build_stubbed(:payment)
|
|
payment.state = 'processing'
|
|
|
|
expect(payment).not_to receive(:authorize!)
|
|
expect(payment).not_to receive(:purchase!)
|
|
expect(payment.process!).to be_nil
|
|
end
|
|
end
|
|
|
|
context "with source required" do
|
|
context "raises an error if no source is specified" do
|
|
specify do
|
|
payment = build_stubbed(:payment, source: nil, payment_method:)
|
|
expect {
|
|
payment.process!
|
|
}.to raise_error(Spree::Core::GatewayError, Spree.t(:payment_processing_failed))
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with source optional" do
|
|
context "raises no error if source is not specified" do
|
|
specify do
|
|
payment = build_stubbed(:payment, source: nil, payment_method:)
|
|
allow(payment.payment_method).to receive(:source_required?) { false }
|
|
expect { payment.process! }.not_to raise_error
|
|
end
|
|
end
|
|
end
|
|
|
|
context "#credit_allowed" do
|
|
it "is the difference between offsets total and payment amount" do
|
|
payment = build_stubbed(:payment, amount: 100)
|
|
allow(payment).to receive(:offsets_total) { 0 }
|
|
expect(payment.credit_allowed).to eq(100)
|
|
allow(payment).to receive(:offsets_total) { 80 }
|
|
expect(payment.credit_allowed).to eq(20)
|
|
end
|
|
end
|
|
|
|
context "#save" do
|
|
context "completed payments" do
|
|
it "updates order payment total" do
|
|
payment = create(:payment, :completed, amount: 100, order:)
|
|
expect(order.payment_total).to eq payment.amount
|
|
end
|
|
end
|
|
|
|
context "non-completed payments" do
|
|
it "doesn't update order payment total" do
|
|
expect {
|
|
create(:payment, amount: 100, order:)
|
|
}.not_to change { order.payment_total }
|
|
end
|
|
end
|
|
|
|
context 'when the payment was completed but now void' do
|
|
let(:payment) { create(:payment, :completed, amount: 100, order:) }
|
|
|
|
it 'updates order payment total' do
|
|
payment.void
|
|
expect(order.payment_total).to eq 0
|
|
end
|
|
end
|
|
|
|
context "completed orders" do
|
|
let(:order_updater) { OrderManagement::Order::Updater.new(order) }
|
|
|
|
before { allow(order).to receive(:completed?) { true } }
|
|
|
|
it "updates payment_state and shipments" do
|
|
expect(OrderManagement::Order::Updater).to receive(:new).with(order).
|
|
and_return(order_updater)
|
|
|
|
expect(order_updater).to receive(:after_payment_update).with(kind_of(Spree::Payment)).
|
|
and_call_original
|
|
|
|
expect(order_updater).to receive(:update_payment_state)
|
|
expect(order_updater).to receive(:update_shipment_state)
|
|
create(:payment, amount: 100, order:)
|
|
end
|
|
end
|
|
|
|
context "when profiles are supported" do
|
|
before do
|
|
allow(payment_method).to receive(:payment_profiles_supported?) { true }
|
|
allow(payment.source).to receive(:has_payment_profile?) { false }
|
|
end
|
|
|
|
context "when there is an error connecting to the gateway" do
|
|
it "should call gateway_error " do
|
|
pending '[Spree build] Failing spec'
|
|
message = double("gateway_error")
|
|
connection_error = ActiveMerchant::ConnectionError.new(message, nil)
|
|
expect(payment_method).to receive(:create_profile).and_raise(connection_error)
|
|
expect do
|
|
Spree::Payment.create(
|
|
amount: 100,
|
|
order:,
|
|
source: card,
|
|
payment_method:
|
|
)
|
|
end.to raise_error(Spree::Core::GatewayError)
|
|
end
|
|
end
|
|
|
|
context "when successfully connecting to the gateway" do
|
|
it "should create a payment profile" do
|
|
payment_method.name = 'Gateway'
|
|
payment_method.distributors << create(:distributor_enterprise)
|
|
payment_method.save!
|
|
|
|
payment.payment_method = payment_method
|
|
payment.source.save_requested_by_customer = true
|
|
|
|
expect(payment_method).to receive(:create_profile)
|
|
|
|
Spree::Payment.create(
|
|
amount: 100,
|
|
order: create(:order),
|
|
source: card,
|
|
payment_method:
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when profiles are not supported" do
|
|
before do
|
|
allow(payment_method).to receive(:payment_profiles_supported?) { false }
|
|
end
|
|
|
|
it "should not create a payment profile" do
|
|
payment_method.name = 'Gateway'
|
|
payment_method.distributors << create(:distributor_enterprise)
|
|
payment_method.save!
|
|
|
|
expect(payment_method).not_to receive :create_profile
|
|
payment = Spree::Payment.create(
|
|
amount: 100,
|
|
order: create(:order),
|
|
source: card,
|
|
payment_method:
|
|
)
|
|
end
|
|
end
|
|
|
|
context 'when the payment was completed but now void' do
|
|
let(:payment) { create(:payment, :completed, amount: 100, order:) }
|
|
|
|
it 'updates order payment total' do
|
|
payment.void
|
|
expect(order.payment_total).to eq 0
|
|
end
|
|
end
|
|
end
|
|
|
|
context "#build_source" do
|
|
it "should build the payment's source" do
|
|
params = { amount: 100, payment_method:,
|
|
source_attributes: {
|
|
expiry: "1 / 99",
|
|
number: '1234567890123',
|
|
verification_value: '123'
|
|
} }
|
|
|
|
payment = Spree::Payment.new(params)
|
|
expect(payment).to be_valid
|
|
expect(payment.source).not_to be_nil
|
|
end
|
|
|
|
it "errors when payment source not valid" do
|
|
params = { amount: 100, payment_method:,
|
|
source_attributes: { expiry: "1 / 12" } }
|
|
|
|
payment = Spree::Payment.new(params)
|
|
expect(payment).not_to be_valid
|
|
expect(payment.source).not_to be_nil
|
|
expect(payment.source.errors[:number]).not_to be_empty
|
|
expect(payment.source.errors[:verification_value]).not_to be_empty
|
|
end
|
|
end
|
|
|
|
context "#currency" do
|
|
it "returns the order currency" do
|
|
payment = build_stubbed(:payment, order: build_stubbed(:order, currency: "ABC"))
|
|
expect(payment.currency).to eq("ABC")
|
|
end
|
|
end
|
|
|
|
context "#display_amount" do
|
|
it "returns a Spree::Money for this amount" do
|
|
payment = build_stubbed(:payment)
|
|
expect(payment.display_amount).to eq(Spree::Money.new(payment.amount))
|
|
end
|
|
end
|
|
|
|
# Regression test for #2216
|
|
context "#gateway_options" do
|
|
before do
|
|
allow(order).to receive(:last_ip_address) { "192.168.1.1" }
|
|
end
|
|
|
|
it "contains an IP" do
|
|
expect(payment.gateway_options[:ip]).to eq(order.last_ip_address)
|
|
end
|
|
end
|
|
|
|
context "#set_unique_identifier" do
|
|
# Regression test for Spree #1998
|
|
it "sets a unique identifier on create" do
|
|
payment.run_callbacks(:create)
|
|
expect(payment.identifier).not_to be_blank
|
|
expect(payment.identifier.size).to eq(8)
|
|
expect(payment.identifier).to be_a(String)
|
|
end
|
|
|
|
# Regression test for Spree #3733
|
|
it "does not regenerate the identifier on re-save" do
|
|
payment.save
|
|
old_identifier = payment.identifier
|
|
payment.save
|
|
expect(payment.identifier).to eq old_identifier
|
|
end
|
|
|
|
context "other payment exists" do
|
|
let(:other_payment) {
|
|
payment_method.name = 'Gateway'
|
|
payment_method.distributors << create(:distributor_enterprise)
|
|
payment_method.save!
|
|
|
|
payment = Spree::Payment.new
|
|
payment.source = card
|
|
payment.order = create(:order)
|
|
payment.payment_method = payment_method
|
|
payment
|
|
}
|
|
|
|
before { other_payment.save! }
|
|
|
|
it "doesn't set duplicate identifier" do
|
|
allow(payment).to receive(:generate_identifier).and_return(other_payment.identifier)
|
|
allow(payment).to receive(:generate_identifier).and_call_original
|
|
|
|
payment.run_callbacks(:create)
|
|
|
|
expect(payment.identifier).not_to be_blank
|
|
expect(payment.identifier).not_to eq(other_payment.identifier)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "available actions" do
|
|
context "for most gateways" do
|
|
let(:payment) { build_stubbed(:payment, source: build_stubbed(:credit_card)) }
|
|
|
|
it "can capture and void" do
|
|
expect(payment.actions).to match_array %w(capture_and_complete_order void)
|
|
end
|
|
|
|
describe "when a payment has been taken" do
|
|
before do
|
|
allow(payment).to receive(:state) { 'completed' }
|
|
allow(payment).to receive(:order) { double(:order, payment_state: 'credit_owed') }
|
|
end
|
|
|
|
it "can void and credit" do
|
|
expect(payment.actions).to match_array %w(void credit)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "refund!" do
|
|
let(:payment) { create(:payment) }
|
|
let(:success) { double(success?: true, authorization: 'abc123') }
|
|
let(:failure) { double(success?: false) }
|
|
|
|
it "always checks the environment" do
|
|
allow(payment.payment_method).to receive(:refund) { success }
|
|
expect(payment).to receive(:check_environment)
|
|
payment.refund!
|
|
end
|
|
|
|
describe "calculating refund amount" do
|
|
let(:payment) { build_stubbed(:payment) }
|
|
|
|
it "returns the parameter amount when given" do
|
|
expect(payment.__send__(:calculate_refund_amount, 123)).to eq(123)
|
|
end
|
|
|
|
it "refunds up to the value of the payment when the outstanding balance is larger" do
|
|
allow(payment).to receive(:credit_allowed) { 123 }
|
|
allow(payment).to receive(:order) { double(:order, outstanding_balance: 1000) }
|
|
expect(payment.__send__(:calculate_refund_amount)).to eq(123)
|
|
end
|
|
|
|
it "refunds up to the outstanding balance of the order when the payment is larger" do
|
|
allow(payment).to receive(:credit_allowed) { 1000 }
|
|
allow(payment).to receive(:order) { double(:order, outstanding_balance: 123) }
|
|
expect(payment.__send__(:calculate_refund_amount)).to eq(123)
|
|
end
|
|
end
|
|
|
|
describe "performing refunds" do
|
|
before do
|
|
allow(payment).to receive(:calculate_refund_amount) { 123 }
|
|
expect(payment.payment_method).to receive(:refund).and_return(success)
|
|
end
|
|
|
|
it "performs the refund without payment profiles" do
|
|
allow(payment.payment_method).to receive(:payment_profiles_supported?) { false }
|
|
payment.refund!
|
|
end
|
|
|
|
it "performs the refund with payment profiles" do
|
|
allow(payment.payment_method).to receive(:payment_profiles_supported?) { true }
|
|
payment.refund!
|
|
end
|
|
end
|
|
|
|
it "records the response" do
|
|
allow(payment).to receive(:calculate_refund_amount) { 123 }
|
|
allow(payment.payment_method).to receive(:refund).and_return(success)
|
|
expect(payment).to receive(:record_response).with(success)
|
|
payment.refund!
|
|
end
|
|
|
|
it "records a payment on success" do
|
|
allow(payment).to receive(:calculate_refund_amount) { 123 }
|
|
allow(payment.payment_method).to receive(:refund).and_return(success)
|
|
allow(payment).to receive(:record_response)
|
|
|
|
expect do
|
|
payment.refund!
|
|
end.to change { Spree::Payment.count }.by(1)
|
|
|
|
p = Spree::Payment.last
|
|
expect(p.order).to eq(payment.order)
|
|
expect(p.source).to eq(payment)
|
|
expect(p.payment_method).to eq(payment.payment_method)
|
|
expect(p.amount).to eq(-123)
|
|
expect(p.response_code).to eq(success.authorization)
|
|
expect(p.state).to eq('completed')
|
|
end
|
|
|
|
it "logs the error on failure" do
|
|
allow(payment).to receive(:calculate_refund_amount) { 123 }
|
|
allow(payment.payment_method).to receive(:refund).and_return(failure)
|
|
allow(payment).to receive(:record_response)
|
|
expect(payment).to receive(:gateway_error).with(failure)
|
|
payment.refund!
|
|
end
|
|
end
|
|
|
|
describe "applying transaction fees" do
|
|
let!(:order) { create(:order) }
|
|
let!(:line_item) { create(:line_item, order:, quantity: 3, price: 5.00) }
|
|
|
|
before do
|
|
order.reload.update_order!
|
|
end
|
|
|
|
context "when order-based calculator" do
|
|
let!(:shop) { create(:enterprise) }
|
|
let!(:payment_method) { create(:payment_method, calculator:) }
|
|
let!(:calculator) do
|
|
Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10)
|
|
end
|
|
|
|
context "when order complete" do
|
|
let!(:order) { create(:completed_order_with_totals, distributor: shop) }
|
|
let!(:variant) { order.line_items.first.variant }
|
|
let!(:inventory_item) { create(:inventory_item, enterprise: shop, variant:) }
|
|
|
|
it "creates adjustment" do
|
|
payment = create(:payment, order:, payment_method:,
|
|
amount: order.total)
|
|
expect(payment.adjustment).to be_present
|
|
expect(payment.adjustment.amount).not_to eq(0)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'OFN specs from previously decorated model' do
|
|
describe "applying transaction fees" do
|
|
let!(:order) { create(:order) }
|
|
let!(:line_item) { create(:line_item, order:, quantity: 3, price: 5.00) }
|
|
|
|
before do
|
|
order.reload.update_order!
|
|
end
|
|
|
|
context "to Stripe payments" do
|
|
let(:shop) { create(:enterprise) }
|
|
let(:payment_method) {
|
|
create(:stripe_sca_payment_method, distributor_ids: [create(:distributor_enterprise).id],
|
|
preferred_enterprise_id: shop.id)
|
|
}
|
|
let(:payment) {
|
|
create(:payment, order:, payment_method:, amount: order.total)
|
|
}
|
|
let(:calculator) { Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10) }
|
|
|
|
before do
|
|
payment_method.calculator = calculator
|
|
payment_method.save!
|
|
|
|
allow(order).to receive(:pending_payments) { [payment] }
|
|
end
|
|
|
|
context "when the payment fails" do
|
|
let(:failed_response) {
|
|
ActiveMerchant::Billing::Response.new(false, "This is an error message")
|
|
}
|
|
|
|
before do
|
|
allow(payment_method).to receive(:purchase) { failed_response }
|
|
end
|
|
|
|
it "makes the transaction fee ineligible and finalizes it" do
|
|
# Decided to wrap the save process in order.process_payments!
|
|
# since that is the context it is usually performed in
|
|
order.process_payments!
|
|
expect(order.payments.count).to eq 1
|
|
expect(order.payments).to include payment
|
|
expect(payment.state).to eq "failed"
|
|
expect(payment.adjustment.eligible?).to be false
|
|
expect(payment.adjustment.finalized?).to be true
|
|
expect(order.all_adjustments.payment_fee.count).to eq 1
|
|
expect(order.all_adjustments.payment_fee.eligible).not_to include payment.adjustment
|
|
end
|
|
end
|
|
|
|
context "when the payment information is invalid" do
|
|
before do
|
|
allow(payment_method).to receive(:supports?) { false }
|
|
end
|
|
|
|
it "makes the transaction fee ineligible and finalizes it" do
|
|
# Decided to wrap the save process in order.process_payments!
|
|
# since that is the context it is usually performed in
|
|
order.process_payments!
|
|
expect(order.payments.count).to eq 1
|
|
expect(order.payments).to include payment
|
|
expect(payment.state).to eq "invalid"
|
|
expect(payment.adjustment.eligible?).to be false
|
|
expect(payment.adjustment.finalized?).to be true
|
|
expect(order.all_adjustments.payment_fee.count).to eq 1
|
|
expect(order.all_adjustments.payment_fee.eligible).not_to include payment.adjustment
|
|
end
|
|
end
|
|
|
|
context "when the payment is processed successfully" do
|
|
let(:successful_response) { ActiveMerchant::Billing::Response.new(true, "Yay!") }
|
|
|
|
before do
|
|
allow(payment_method).to receive(:purchase) { successful_response }
|
|
end
|
|
|
|
it "creates an appropriate adjustment" do
|
|
# Decided to wrap the save process in order.process_payments!
|
|
# since that is the context it is usually performed in
|
|
order.process_payments!
|
|
expect(order.payments.count).to eq 1
|
|
expect(order.payments).to include payment
|
|
expect(payment.state).to eq "completed"
|
|
expect(payment.adjustment.eligible?).to be true
|
|
expect(order.all_adjustments.payment_fee.count).to eq 1
|
|
expect(order.all_adjustments.payment_fee.eligible).to include payment.adjustment
|
|
expect(payment.adjustment.amount).to eq 1.5
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#clear_authorization_url" do
|
|
let(:payment) { create(:payment, redirect_auth_url: "auth_url") }
|
|
|
|
it "removes the redirect_auth_url" do
|
|
payment.clear_authorization_url
|
|
expect(payment.redirect_auth_url).to eq(nil)
|
|
end
|
|
end
|
|
|
|
describe "#complete" do
|
|
let(:payment) { build(:payment, state: "processing") }
|
|
|
|
it "sets :captured_at to the current time" do
|
|
payment.complete
|
|
expect(payment.captured_at).to be_present
|
|
end
|
|
end
|
|
end
|