Skip to content

Commit

Permalink
Add Cashfree Payment Provider
Browse files Browse the repository at this point in the history
  • Loading branch information
AyushChothe committed Sep 24, 2024
1 parent f6e4810 commit f732cbd
Show file tree
Hide file tree
Showing 37 changed files with 1,175 additions and 5 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::GocardlessService.new.handle_incoming_webhook(
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 @@ -113,6 +113,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 @@ -45,10 +45,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 @@ -45,6 +45,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 @@ -47,12 +47,13 @@ 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'

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 @@ -121,6 +122,8 @@ def provider_customer
stripe_customer
when :gocardless
gocardless_customer
when :cashfree
cashfree_customer
when :adyen
adyen_customer
end
Expand Down
3 changes: 3 additions & 0 deletions app/models/organization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class Organization < ApplicationRecord

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 :netsuite_integrations, class_name: 'Integrations::NetsuiteIntegration'
Expand Down Expand Up @@ -106,6 +107,8 @@ def payment_provider(provider)
stripe_payment_provider
when 'gocardless'
gocardless_payment_provider
when 'cashfree'
cashfree_payment_provider
when 'adyen'
adyen_payment_provider
end
Expand Down
6 changes: 6 additions & 0 deletions app/models/payment_provider_customers/cashfree_customer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true

module PaymentProviderCustomers
class CashfreeCustomer < BaseCustomer
end
end
15 changes: 15 additions & 0 deletions app/models/payment_providers/cashfree_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 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
3 changes: 3 additions & 0 deletions app/serializers/v1/customer_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,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 @@ -277,7 +277,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]
end
end
Expand All @@ -304,6 +304,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
18 changes: 18 additions & 0 deletions app/services/customers/update_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ def create_or_update_provider_customer(customer, payment_provider, billing_confi
return unless handle_provider_customer

update_gocardless_customer(customer, billing_configuration)
when 'cashfree'
handle_provider_customer ||= customer.cashfree_customer&.provider_customer_id.present?

return unless handle_provider_customer

update_cashfree_customer(customer, billing_configuration)
when 'adyen'
handle_provider_customer ||= customer.adyen_customer&.provider_customer_id.present?

Expand Down Expand Up @@ -219,6 +225,18 @@ def update_gocardless_customer(customer, billing_configuration)
customer.gocardless_customer&.reload
end

def update_cashfree_customer(customer, billing_configuration)
create_result = PaymentProviderCustomers::CreateService.new(customer).create_or_update(
customer_class: PaymentProviderCustomers::CashfreeCustomer,
payment_provider_id: payment_provider(customer)&.id,
params: billing_configuration
)
create_result.raise_if_error!

# NOTE: Create service is modifying an other instance of the provider customer
customer.cashfree_customer&.reload
end

def update_adyen_customer(customer, billing_configuration)
create_result = PaymentProviderCustomers::CreateService.new(customer).create_or_update(
customer_class: PaymentProviderCustomers::AdyenCustomer,
Expand Down
Loading

0 comments on commit f732cbd

Please sign in to comment.