diff --git a/app/services/payment_providers/cashfree_service.rb b/app/services/payment_providers/cashfree_service.rb index 82c2752f8af3..877abeae84c3 100644 --- a/app/services/payment_providers/cashfree_service.rb +++ b/app/services/payment_providers/cashfree_service.rb @@ -11,7 +11,7 @@ def create_or_update(**args) organization_id: args[:organization].id, code: args[:code], id: args[:id], - payment_provider_type: 'cashfree' + payment_provider_type: "cashfree" ) cashfree_provider = if payment_provider_result.success? @@ -23,6 +23,8 @@ def create_or_update(**args) ) end + old_code = cashfree_provider.code + cashfree_provider.client_id = args[:client_id] if args.key?(:client_id) cashfree_provider.client_secret = args[:client_secret] if args.key?(:client_secret) cashfree_provider.success_redirect_url = args[:success_redirect_url] if args.key?(:success_redirect_url) @@ -30,6 +32,10 @@ def create_or_update(**args) cashfree_provider.name = args[:name] if args.key?(:name) cashfree_provider.save! + if payment_provider_code_changed?(cashfree_provider, old_code, args) + cashfree_provider.customers.update_all(payment_provider_code: args[:code]) # rubocop:disable Rails/SkipsModelValidations + end + result.cashfree_provider = cashfree_provider result rescue ActiveRecord::RecordInvalid => e @@ -40,17 +46,17 @@ def handle_incoming_webhook(organization_id:, body:, timestamp:, signature:, cod payment_provider_result = PaymentProviders::FindService.call( organization_id:, code:, - payment_provider_type: 'cashfree' + payment_provider_type: "cashfree" ) return payment_provider_result unless payment_provider_result.success? secret_key = payment_provider_result.payment_provider.client_secret data = "#{timestamp}#{body}" - gen_signature = Base64.strict_encode64(OpenSSL::HMAC.digest('sha256', secret_key, data)) + gen_signature = Base64.strict_encode64(OpenSSL::HMAC.digest("sha256", secret_key, data)) unless gen_signature == signature - return result.service_failure!(code: 'webhook_error', message: 'Invalid signature') + return result.service_failure!(code: "webhook_error", message: "Invalid signature") end PaymentProviders::Cashfree::HandleEventJob.perform_later(event_json: body) @@ -61,12 +67,12 @@ def handle_incoming_webhook(organization_id:, body:, timestamp:, signature:, cod def handle_event(event_json:) event = JSON.parse(event_json) - event_type = event['type'] + event_type = event["type"] case event_type - when 'PAYMENT_LINK_EVENT' - link_status = event.dig('data', 'link_status') - provider_payment_id = event.dig('data', 'link_notes', 'lago_invoice_id') + when "PAYMENT_LINK_EVENT" + link_status = event.dig("data", "link_status") + provider_payment_id = event.dig("data", "link_notes", "lago_invoice_id") if LINK_STATUS_ACTIONS.include?(link_status) && !provider_payment_id.nil? update_payment_status_result = Invoices::Payments::CashfreeService diff --git a/spec/services/payment_providers/cashfree_service_spec.rb b/spec/services/payment_providers/cashfree_service_spec.rb index 673328ce80e7..5673a8956c2e 100644 --- a/spec/services/payment_providers/cashfree_service_spec.rb +++ b/spec/services/payment_providers/cashfree_service_spec.rb @@ -1,20 +1,20 @@ # frozen_string_literal: true -require 'rails_helper' +require "rails_helper" RSpec.describe PaymentProviders::CashfreeService, type: :service do subject(:cashfree_service) { described_class.new(membership.user) } let(:membership) { create(:membership) } let(:organization) { membership.organization } - let(:code) { 'code_1' } - let(:name) { 'Name 1' } - let(:client_id) { '123456_abc' } - let(:client_secret) { 'cfsk_ma_prod_abc_123456' } + let(:code) { "code_1" } + let(:name) { "Name 1" } + let(:client_id) { "123456_abc" } + let(:client_secret) { "cfsk_ma_prod_abc_123456" } let(:success_redirect_url) { Faker::Internet.url } - describe '.create_or_update' do - it 'creates a cashfree provider' do + describe ".create_or_update" do + it "creates a cashfree provider" do expect do cashfree_service.create_or_update( organization:, @@ -27,14 +27,47 @@ end.to change(PaymentProviders::CashfreeProvider, :count).by(1) end - context 'when organization already have a cashfree provider' do + context "when code was changed" do + let(:new_code) { "updated_code_1" } + let(:cashfree_customer) { create(:cashfree_customer, payment_provider:, customer:) } + let(:customer) { create(:customer, organization:) } + + let(:payment_provider) do + create( + :cashfree_provider, + organization:, + code:, + name:, + client_secret: "secret" + ) + end + + before { cashfree_customer } + + it "updates payment provider codes of all customers" do + result = cashfree_service.create_or_update( + id: payment_provider.id, + organization:, + code: new_code, + name:, + client_secret: "secret" + ) + + aggregate_failures do + expect(result).to be_success + expect(result.cashfree_provider.customers.first.payment_provider_code).to eq(new_code) + end + end + end + + context "when organization already have a cashfree provider" do let(:cashfree_provider) do - create(:cashfree_provider, organization:, client_id: '123456_abc_old', client_secret: 'cfsk_ma_prod_abc_123456_old', code:) + create(:cashfree_provider, organization:, client_id: "123456_abc_old", client_secret: "cfsk_ma_prod_abc_123456_old", code:) end before { cashfree_provider } - it 'updates the existing provider' do + it "updates the existing provider" do result = cashfree_service.create_or_update( organization:, code:, @@ -48,8 +81,8 @@ aggregate_failures do expect(result.cashfree_provider.id).to eq(cashfree_provider.id) - expect(result.cashfree_provider.client_id).to eq('123456_abc') - expect(result.cashfree_provider.client_secret).to eq('cfsk_ma_prod_abc_123456') + expect(result.cashfree_provider.client_id).to eq("123456_abc") + expect(result.cashfree_provider.client_secret).to eq("cfsk_ma_prod_abc_123456") expect(result.cashfree_provider.code).to eq(code) expect(result.cashfree_provider.name).to eq(name) expect(result.cashfree_provider.success_redirect_url).to eq(success_redirect_url) @@ -57,10 +90,10 @@ end end - context 'with validation error' do + context "with validation error" do let(:token) { nil } - it 'returns an error result' do + it "returns an error result" do result = cashfree_service.create_or_update( organization: ) @@ -68,29 +101,29 @@ aggregate_failures do expect(result).not_to be_success expect(result.error).to be_a(BaseService::ValidationFailure) - expect(result.error.messages[:client_id]).to eq(['value_is_mandatory']) - expect(result.error.messages[:client_secret]).to eq(['value_is_mandatory']) + expect(result.error.messages[:client_id]).to eq(["value_is_mandatory"]) + expect(result.error.messages[:client_secret]).to eq(["value_is_mandatory"]) end end end end - describe '.handle_incoming_webhook' do + describe ".handle_incoming_webhook" do let(:cashfree_provider) { create(:cashfree_provider, organization:, client_id:, client_secret:) } let(:body) do - path = Rails.root.join('spec/fixtures/cashfree/event.json') + path = Rails.root.join("spec/fixtures/cashfree/event.json") File.read(path) end before { cashfree_provider } - it 'checks the webhook' do + it "checks the webhook" do result = cashfree_service.handle_incoming_webhook( organization_id: organization.id, body:, - timestamp: '1629271506', - signature: 'MFB3Rkubs4jB97ROS/I4iu9llAAP5ykJ3GZYp95o/Mw=' + timestamp: "1629271506", + signature: "MFB3Rkubs4jB97ROS/I4iu9llAAP5ykJ3GZYp95o/Mw=" ) expect(result).to be_success @@ -98,26 +131,26 @@ expect(PaymentProviders::Cashfree::HandleEventJob).to have_been_enqueued end - context 'when failing to validate the signature' do - it 'returns an error' do + context "when failing to validate the signature" do + it "returns an error" do result = cashfree_service.handle_incoming_webhook( organization_id: organization.id, body:, - timestamp: '1629271506', - signature: 'signature' + timestamp: "1629271506", + signature: "signature" ) aggregate_failures do expect(result).not_to be_success expect(result.error).to be_a(BaseService::ServiceFailure) - expect(result.error.code).to eq('webhook_error') - expect(result.error.error_message).to eq('Invalid signature') + expect(result.error.code).to eq("webhook_error") + expect(result.error.error_message).to eq("Invalid signature") end end end end - describe '.handle_event' do + describe ".handle_event" do let(:payment_service) { instance_double(Invoices::Payments::CashfreeService) } let(:service_result) { BaseService::Result.new } @@ -128,13 +161,13 @@ .and_return(service_result) end - context 'when succeeded payment event' do + context "when succeeded payment event" do let(:event) do - path = Rails.root.join('spec/fixtures/cashfree/event.json') + path = Rails.root.join("spec/fixtures/cashfree/event.json") File.read(path) end - it 'routes the event to an other service' do + it "routes the event to an other service" do cashfree_service.handle_event(event_json: event) expect(Invoices::Payments::CashfreeService).to have_received(:new)