Skip to content

Commit

Permalink
Add Cashfree Payment Provider
Browse files Browse the repository at this point in the history
  • Loading branch information
AyushChothe authored and vincent-pochet committed Oct 31, 2024
1 parent be2422e commit 2d6c703
Show file tree
Hide file tree
Showing 43 changed files with 1,445 additions and 15 deletions.
20 changes: 20 additions & 0 deletions app/controllers/webhooks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,26 @@ def stripe
head(:ok)
end

def cashfree
result = PaymentProviders::CashfreeService.new.handle_incoming_webhook(
organization_id: params[:organization_id],
code: params[:code].presence,
body: request.body.read,
timestamp: request.headers['X-Cashfree-Timestamp'],
signature: request.headers['X-Cashfree-Signature']
)

unless result.success?
if result.error.is_a?(BaseService::ServiceFailure) && result.error.code == 'webhook_error'
return head(:bad_request)
end

result.raise_if_error!
end

head(:ok)
end

def gocardless
result = PaymentProviders::Gocardless::HandleIncomingWebhookService.call(
organization_id: params[:organization_id],
Expand Down
20 changes: 20 additions & 0 deletions app/graphql/mutations/payment_providers/cashfree/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

module Mutations
module PaymentProviders
module Cashfree
class Base < BaseMutation
include AuthenticableApiUser
include RequiredOrganization

def resolve(**args)
result = ::PaymentProviders::CashfreeService
.new(context[:current_user])
.create_or_update(**args.merge(organization: current_organization))

result.success? ? result.cashfree_provider : result_error(result)
end
end
end
end
end
18 changes: 18 additions & 0 deletions app/graphql/mutations/payment_providers/cashfree/create.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module Mutations
module PaymentProviders
module Cashfree
class Create < Base
REQUIRED_PERMISSION = 'organization:integrations:create'

graphql_name 'AddCashfreePaymentProvider'
description 'Add or update Cashfree payment provider'

input_object_class Types::PaymentProviders::CashfreeInput

type Types::PaymentProviders::Cashfree
end
end
end
end
18 changes: 18 additions & 0 deletions app/graphql/mutations/payment_providers/cashfree/update.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module Mutations
module PaymentProviders
module Cashfree
class Update < Base
REQUIRED_PERMISSION = 'organization:integrations:update'

graphql_name 'UpdateCashfreePaymentProvider'
description 'Update Cashfree payment provider'

input_object_class Types::PaymentProviders::UpdateInput

type Types::PaymentProviders::Cashfree
end
end
end
end
2 changes: 2 additions & 0 deletions app/graphql/resolvers/payment_providers_resolver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ def provider_type(type)
PaymentProviders::StripeProvider.to_s
when 'gocardless'
PaymentProviders::GocardlessProvider.to_s
when 'cashfree'
PaymentProviders::CashfreeProvider.to_s
else
raise(NotImplementedError)
end
Expand Down
2 changes: 2 additions & 0 deletions app/graphql/types/customers/object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ def provider_customer
object.stripe_customer
when :gocardless
object.gocardless_customer
when :cashfree
object.cashfree_customer
when :adyen
object.adyen_customer
end
Expand Down
2 changes: 2 additions & 0 deletions app/graphql/types/mutation_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,12 @@ class MutationType < Types::BaseObject
field :update_add_on, mutation: Mutations::AddOns::Update

field :add_adyen_payment_provider, mutation: Mutations::PaymentProviders::Adyen::Create
field :add_cashfree_payment_provider, mutation: Mutations::PaymentProviders::Cashfree::Create
field :add_gocardless_payment_provider, mutation: Mutations::PaymentProviders::Gocardless::Create
field :add_stripe_payment_provider, mutation: Mutations::PaymentProviders::Stripe::Create

field :update_adyen_payment_provider, mutation: Mutations::PaymentProviders::Adyen::Update
field :update_cashfree_payment_provider, mutation: Mutations::PaymentProviders::Cashfree::Update
field :update_gocardless_payment_provider, mutation: Mutations::PaymentProviders::Gocardless::Update
field :update_stripe_payment_provider, mutation: Mutations::PaymentProviders::Stripe::Update

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class CurrentOrganizationType < BaseOrganizationType
field :taxes, [Types::Taxes::Object], resolver: Resolvers::TaxesResolver, permission: 'organization:taxes:view'

field :adyen_payment_providers, [Types::PaymentProviders::Adyen], permission: 'organization:integrations:view'
field :cashfree_payment_providers, [Types::PaymentProviders::Cashfree], permission: 'organization:integrations:view'
field :gocardless_payment_providers, [Types::PaymentProviders::Gocardless], permission: 'organization:integrations:view'
field :stripe_payment_providers, [Types::PaymentProviders::Stripe], permission: 'organization:integrations:view'

Expand Down
17 changes: 17 additions & 0 deletions app/graphql/types/payment_providers/cashfree.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

module Types
module PaymentProviders
class Cashfree < Types::BaseObject
graphql_name 'CashfreeProvider'

field :code, String, null: false
field :id, ID, null: false
field :name, String, null: false

field :client_id, String, null: true, permission: 'organization:integrations:view'
field :client_secret, String, null: true, permission: 'organization:integrations:view'
field :success_redirect_url, String, null: true, permission: 'organization:integrations:view'
end
end
end
15 changes: 15 additions & 0 deletions app/graphql/types/payment_providers/cashfree_input.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module Types
module PaymentProviders
class CashfreeInput < BaseInputObject
description 'Cashfree input arguments'

argument :client_id, String, required: true
argument :client_secret, String, required: true
argument :code, String, required: true
argument :name, String, required: true
argument :success_redirect_url, String, required: false
end
end
end
5 changes: 4 additions & 1 deletion app/graphql/types/payment_providers/object.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ class Object < Types::BaseUnion

possible_types Types::PaymentProviders::Adyen,
Types::PaymentProviders::Gocardless,
Types::PaymentProviders::Stripe
Types::PaymentProviders::Stripe,
Types::PaymentProviders::Cashfree

def self.resolve_type(object, _context)
case object.class.to_s
Expand All @@ -17,6 +18,8 @@ def self.resolve_type(object, _context)
Types::PaymentProviders::Stripe
when 'PaymentProviders::GocardlessProvider'
Types::PaymentProviders::Gocardless
when 'PaymentProviders::CashfreeProvider'
Types::PaymentProviders::Cashfree
else
raise "Unexpected Payment provider type: #{object.inspect}"
end
Expand Down
16 changes: 16 additions & 0 deletions app/jobs/invoices/payments/cashfree_create_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

module Invoices
module Payments
class CashfreeCreateJob < ApplicationJob
queue_as 'providers'

unique :until_executed

def perform(invoice)
result = Invoices::Payments::CashfreeService.new(invoice).create
result.raise_if_error!
end
end
end
end
14 changes: 14 additions & 0 deletions app/jobs/payment_providers/cashfree/handle_event_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# frozen_string_literal: true

module PaymentProviders
module Cashfree
class HandleEventJob < ApplicationJob
queue_as 'providers'

def perform(event_json:)
result = PaymentProviders::CashfreeService.new.handle_event(event_json:)
result.raise_if_error!
end
end
end
end
5 changes: 4 additions & 1 deletion app/models/customer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,14 @@ class Customer < ApplicationRecord

has_one :stripe_customer, class_name: 'PaymentProviderCustomers::StripeCustomer'
has_one :gocardless_customer, class_name: 'PaymentProviderCustomers::GocardlessCustomer'
has_one :cashfree_customer, class_name: 'PaymentProviderCustomers::CashfreeCustomer'
has_one :adyen_customer, class_name: 'PaymentProviderCustomers::AdyenCustomer'
has_one :netsuite_customer, class_name: 'IntegrationCustomers::NetsuiteCustomer'
has_one :anrok_customer, class_name: 'IntegrationCustomers::AnrokCustomer'
has_one :xero_customer, class_name: 'IntegrationCustomers::XeroCustomer'
has_one :hubspot_customer, class_name: 'IntegrationCustomers::HubspotCustomer'

PAYMENT_PROVIDERS = %w[stripe gocardless adyen].freeze
PAYMENT_PROVIDERS = %w[stripe gocardless cashfree adyen].freeze

default_scope -> { kept }
sequenced scope: ->(customer) { customer.organization.customers.with_discarded },
Expand Down Expand Up @@ -142,6 +143,8 @@ def provider_customer
stripe_customer
when :gocardless
gocardless_customer
when :cashfree
cashfree_customer
when :adyen
adyen_customer
end
Expand Down
13 changes: 7 additions & 6 deletions app/models/organization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,17 @@ class Organization < ApplicationRecord
has_many :error_details
has_many :dunning_campaigns

has_many :stripe_payment_providers, class_name: "PaymentProviders::StripeProvider"
has_many :gocardless_payment_providers, class_name: "PaymentProviders::GocardlessProvider"
has_many :adyen_payment_providers, class_name: "PaymentProviders::AdyenProvider"
has_many :stripe_payment_providers, class_name: 'PaymentProviders::StripeProvider'
has_many :gocardless_payment_providers, class_name: 'PaymentProviders::GocardlessProvider'
has_many :cashfree_payment_providers, class_name: 'PaymentProviders::CashfreeProvider'
has_many :adyen_payment_providers, class_name: 'PaymentProviders::AdyenProvider'

has_many :hubspot_integrations, class_name: "Integrations::HubspotIntegration"
has_many :netsuite_integrations, class_name: "Integrations::NetsuiteIntegration"
has_many :xero_integrations, class_name: "Integrations::XeroIntegration"

has_one :applied_dunning_campaign, -> { where(applied_to_organization: true) }, class_name: "DunningCampaign"

has_one :applied_dunning_campaign, -> { where(applied_to_organization: true) }, class_name: "DunningCampaign"

has_one_attached :logo

DOCUMENT_NUMBERINGS = [
Expand Down Expand Up @@ -112,7 +111,9 @@ def payment_provider(provider)
stripe_payment_provider
when "gocardless"
gocardless_payment_provider
when "adyen"
when 'cashfree'
cashfree_payment_provider
when 'adyen'
adyen_payment_provider
end
end
Expand Down
31 changes: 31 additions & 0 deletions app/models/payment_provider_customers/cashfree_customer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

module PaymentProviderCustomers
class CashfreeCustomer < BaseCustomer
end
end

# == Schema Information
#
# Table name: payment_provider_customers
#
# id :uuid not null, primary key
# settings :jsonb not null
# type :string not null
# created_at :datetime not null
# updated_at :datetime not null
# customer_id :uuid not null
# payment_provider_id :uuid
# provider_customer_id :string
#
# Indexes
#
# index_payment_provider_customers_on_customer_id_and_type (customer_id,type) UNIQUE
# index_payment_provider_customers_on_payment_provider_id (payment_provider_id)
# index_payment_provider_customers_on_provider_customer_id (provider_customer_id)
#
# Foreign Keys
#
# fk_rails_... (customer_id => customers.id)
# fk_rails_... (payment_provider_id => payment_providers.id)
#
39 changes: 39 additions & 0 deletions app/models/payment_providers/cashfree_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

module PaymentProviders
class CashfreeProvider < BaseProvider
SUCCESS_REDIRECT_URL = 'https://cashfree.com/'
API_VERSION = "2023-08-01"
BASE_URL = (Rails.env.production? ? 'https://api.cashfree.com/pg/links' : 'https://sandbox.cashfree.com/pg/links')

validates :client_id, presence: true
validates :client_secret, presence: true
validates :success_redirect_url, url: true, allow_nil: true, length: {maximum: 1024}

secrets_accessors :client_id, :client_secret
end
end

# == Schema Information
#
# Table name: payment_providers
#
# id :uuid not null, primary key
# code :string not null
# name :string not null
# secrets :string
# settings :jsonb not null
# type :string not null
# created_at :datetime not null
# updated_at :datetime not null
# organization_id :uuid not null
#
# Indexes
#
# index_payment_providers_on_code_and_organization_id (code,organization_id) UNIQUE
# index_payment_providers_on_organization_id (organization_id)
#
# Foreign Keys
#
# fk_rails_... (organization_id => organizations.id)
#
3 changes: 3 additions & 0 deletions app/serializers/v1/customer_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ def billing_configuration
when :gocardless
configuration[:provider_customer_id] = model.gocardless_customer&.provider_customer_id
configuration.merge!(model.gocardless_customer&.settings || {})
when :cashfree
configuration[:provider_customer_id] = model.cashfree_customer&.provider_customer_id
configuration.merge!(model.cashfree_customer&.settings || {})
when :adyen
configuration[:provider_customer_id] = model.adyen_customer&.provider_customer_id
configuration.merge!(model.adyen_customer&.settings || {})
Expand Down
4 changes: 3 additions & 1 deletion app/services/customers/create_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ def handle_api_billing_configuration(customer, params, new_customer)

if billing.key?(:payment_provider)
customer.payment_provider = nil
if %w[stripe gocardless adyen].include?(billing[:payment_provider])
if %w[stripe gocardless cashfree adyen].include?(billing[:payment_provider])
customer.payment_provider = billing[:payment_provider]
customer.payment_provider_code = billing[:payment_provider_code] if billing.key?(:payment_provider_code)
end
Expand Down Expand Up @@ -308,6 +308,8 @@ def create_or_update_provider_customer(customer, billing_configuration = {})
PaymentProviderCustomers::StripeCustomer
when 'gocardless'
PaymentProviderCustomers::GocardlessCustomer
when 'cashfree'
PaymentProviderCustomers::CashfreeCustomer
when 'adyen'
PaymentProviderCustomers::AdyenCustomer
end
Expand Down
Loading

0 comments on commit 2d6c703

Please sign in to comment.