Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions app/contracts/capture_contract.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class CaptureContract < Dry::Validation::Contract
# include BaseContract

params do
required(:amount).filled(:decimal)
required(:payment_reference).filled(:string)
end

rule(:payment_reference) do
key.failure(I18n.t('api_errors.invalid_payment_reference')) unless Invoice.exists?(payment_reference: value)
end
end
11 changes: 11 additions & 0 deletions app/contracts/void_contract.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class VoidContract < Dry::Validation::Contract
# include BaseContract

params do
required(:payment_reference).filled(:string)
end

rule(:payment_reference) do
key.failure(I18n.t('api_errors.invalid_payment_reference')) unless Invoice.exists?(payment_reference: value)
end
end
9 changes: 7 additions & 2 deletions app/controllers/api/v1/refund/auction_controller.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
module Api
module V1
module Refund
# rubocop:disable Metrics
class AuctionController < ApplicationController
before_action :load_invoice

def create
response = RefundService.call(amount: @invoice.transaction_amount,
payment_reference: @invoice.payment_reference)
response = if @invoice.payment_method == 'card'
VoidService.call(payment_reference: @invoice.payment_reference)
else
RefundService.call(amount: @invoice.transaction_amount,
payment_reference: @invoice.payment_reference)
end

if response.result?
@invoice.update(status: 'refunded')
Expand Down
33 changes: 33 additions & 0 deletions app/controllers/api/v1/refund/capture_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module Api
module V1
module Refund
class CaptureController < ApplicationController
before_action :load_invoice

def create
unless @invoice.payment_method == 'card'
render json: { message: 'Invoice was not paid by card' }, status: :ok and return
end

response = CaptureService.call(amount: @invoice.transaction_amount, payment_reference: @invoice.payment_reference)

if response.result?
@invoice.update(status: 'captured')
render json: { message: 'Invoice was captured' }, status: :ok
else
render json: { error: response.errors }, status: :unprocessable_entity
end
end

private

def load_invoice
@invoice = ::Invoice.find_by(invoice_number: params[:params][:invoice_number])
return if @invoice.present?

render json: { error: 'Invoice not found' }, status: :not_found
end
end
end
end
end
3 changes: 3 additions & 0 deletions app/models/global_variable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class GlobalVariable
BASE_ENDPOINT = ENV['everypay_base'] || 'https://igw-demo.every-pay.com/api/v4'
ONEOFF_ENDPOINT = '/payments/oneoff'.freeze
ACCOUNT_NAME = ENV['account_name']
DEPOSIT_ACCOUNT_NAME = ENV['deposit_account_name']

INITIATOR = 'billing'.freeze
BILLING_SECRET = ENV['billing_secret']
Expand All @@ -25,4 +26,6 @@ class GlobalVariable
ALLOWED_DEV_BASE_URLS = ENV['allowed_base_urls']

REFUND_ENDPOINT = '/payments/refund'.freeze
VOID_ENDPOINT = '/payments/void'.freeze
CAPTURE_ENDPOINT = '/payments/capture'.freeze
end
51 changes: 26 additions & 25 deletions app/models/invoice.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,35 @@ class Invoice < ApplicationRecord
AUCTION = 'auction'.freeze
EEID = 'eeid'.freeze

store_accessor :everypay_response, :payment_method

pg_search_scope :search_by_number, against: [:invoice_number],
using: {
tsearch: {
prefix: true
}
}
using: {
tsearch: {
prefix: true
}
}

enum affiliation: %i[regular auction_deposit]
enum status: %i[unpaid paid cancelled failed refunded]
enum affiliation: { regular: 0, auction_deposit: 1 }
enum status: { unpaid: 0, paid: 1, cancelled: 2, failed: 3, refunded: 4, captured: 5 }

scope :with_status, ->(status) {
where(status: status) if status.present?
scope :with_status, lambda { |status|
where(status:) if status.present?
}

scope :with_number, ->(invoice_number) {
scope :with_number, lambda { |invoice_number|
search_by_number(invoice_number) if invoice_number.present?
}

scope :with_amount_between, ->(low, high) {
scope :with_amount_between, lambda { |low, high|
where(transaction_amount: low.to_f..high.to_f) if low.present? && high.present?
}

def self.search(params={})
sort_column = params[:sort].presence_in(%w{ invoice_number status affiliation }) || "id"
sort_direction = params[:direction].presence_in(%w{ asc desc }) || "desc"
def self.search(params = {})
sort_column = params[:sort].presence_in(%w[invoice_number status affiliation]) || 'id'
sort_direction = params[:direction].presence_in(%w[asc desc]) || 'desc'

self
.with_number(params[:invoice_number].to_s.downcase)
with_number(params[:invoice_number].to_s.downcase)
.with_status(params[:status])
.with_amount_between(params[:min_amount], params[:max_amount])
.order(sort_column => sort_direction)
Expand All @@ -59,15 +60,15 @@ def auction?

def to_h
{
invoice_number: invoice_number,
initiator: initiator,
payment_reference: payment_reference,
transaction_amount: transaction_amount,
status: status,
in_directo: in_directo,
everypay_response: everypay_response,
transaction_time: transaction_time,
sent_at_omniva: sent_at_omniva
invoice_number:,
initiator:,
payment_reference:,
transaction_amount:,
status:,
in_directo:,
everypay_response:,
transaction_time:,
sent_at_omniva:
}
end
end
3 changes: 2 additions & 1 deletion app/services/auction/base_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module BaseService

AUCTION_DEPOSIT = 'auction_deposit'.freeze

def oneoff_link(bulk: false)
def oneoff_link(bulk: false, deposit_banklink: false)
invoice_number = InvoiceNumberService.call(invoice_auction_deposit: true)
invoice_params = invoice_params(params, invoice_number)

Expand All @@ -13,6 +13,7 @@ def oneoff_link(bulk: false)
customer_url: params[:customer_url],
reference_number: params[:reference_number],
bulk: bulk,
deposit_banklink: deposit_banklink,
bulk_invoices: params[:description].to_s.split(' '))
response.result? ? struct_response(response.instance) : parse_validation_errors(response)
end
Expand Down
2 changes: 1 addition & 1 deletion app/services/auction/deposit_prepayment_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def call
contract = DepositPrepaymentContract.new
result = contract.call(params)

result.success? ? oneoff_link : parse_validation_errors(result)
result.success? ? oneoff_link(deposit_banklink: true) : parse_validation_errors(result)
end
end
end
49 changes: 49 additions & 0 deletions app/services/capture_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
class CaptureService
include Request
include ApplicationService
include ActionView::Helpers::NumberHelper

attr_reader :amount, :payment_reference

def initialize(amount:, payment_reference:)
@amount = amount
@payment_reference = payment_reference
end

def self.call(amount:, payment_reference:)
new(amount:, payment_reference:).call
end

def call
contract = CaptureContract.new
result = contract.call(amount:, payment_reference:)

if result.success?
response = base_request
struct_response(response)
else
parse_validation_errors(result)
end
end

private

def base_request
uri = URI("#{GlobalVariable::BASE_ENDPOINT}#{GlobalVariable::CAPTURE_ENDPOINT}")
post(direction: 'everypay', path: uri, params: body)
end

def body
{
'api_username' => GlobalVariable::API_USERNAME,
'amount' => number_with_precision(amount, precision: 2),
'payment_reference' => payment_reference,
'nonce' => nonce,
'timestamp' => "#{Time.zone.now.to_formatted_s(:iso8601)}"
}
end

def nonce
rand(10**30).to_s.rjust(30, '0')
end
end
47 changes: 25 additions & 22 deletions app/services/oneoff.rb
Original file line number Diff line number Diff line change
@@ -1,44 +1,47 @@
class InvalidParams < StandardError; end

# rubocop:disable Metrics
class Oneoff
include Request
include ApplicationService

attr_reader :invoice_number, :customer_url, :reference_number, :bulk, :bulk_invoices
attr_reader :invoice_number, :customer_url, :reference_number, :bulk, :deposit_banklink, :bulk_invoices

def initialize(invoice_number:, customer_url:, reference_number:, bulk: false, bulk_invoices: [])
@invoice = Invoice.find_by(invoice_number: invoice_number)
def initialize(invoice_number:, customer_url:, reference_number:, bulk: false, deposit_banklink: false, bulk_invoices: [])
@invoice = Invoice.find_by(invoice_number:)

@invoice_number = invoice_number
@customer_url = customer_url
@reference_number = reference_number
@bulk = bulk
@bulk_invoices = bulk_invoices
@deposit_banklink = deposit_banklink
end

def self.call(invoice_number:, customer_url:, reference_number:, bulk: false, bulk_invoices: [])
new(invoice_number: invoice_number,
customer_url: customer_url,
reference_number: reference_number,
bulk: bulk,
bulk_invoices: bulk_invoices).call
def self.call(invoice_number:, customer_url:, reference_number:, bulk: false, deposit_banklink: false, bulk_invoices: [])
new(invoice_number:,
customer_url:,
reference_number:,
bulk:,
deposit_banklink:,
bulk_invoices:).call
end

def call
if @invoice.nil?
if invoice_number.nil?
errors = 'Internal error: called invoice withour number. Please contact to administrator'
else
errors = "Invoice with #{invoice_number} not found in internal system"
end
errors = if invoice_number.nil?
'Internal error: called invoice withour number. Please contact to administrator'
else
"Invoice with #{invoice_number} not found in internal system"
end

return wrap(result: false, instance: nil, errors: errors)
return wrap(result: false, instance: nil, errors:)
end

contract = OneoffParamsContract.new
result = contract.call(invoice_number: invoice_number,
customer_url: customer_url,
reference_number: reference_number)
result = contract.call(invoice_number:,
customer_url:,
reference_number:)
if result.success?
response = base_request
struct_response(response)
Expand All @@ -59,11 +62,11 @@ def body

{
'api_username' => GlobalVariable::API_USERNAME,
'account_name' => GlobalVariable::ACCOUNT_NAME,
'account_name' => deposit_banklink ? GlobalVariable::DEPOSIT_ACCOUNT_NAME : GlobalVariable::ACCOUNT_NAME,
'amount' => @invoice.transaction_amount.to_f,
'order_reference' => "#{ bulk ? bulk_description : @invoice.invoice_number }",
'order_reference' => "#{bulk ? bulk_description : @invoice.invoice_number}",
'token_agreement' => 'unscheduled',
'nonce' => "#{rand(10 ** 30).to_s.rjust(30,'0')}",
'nonce' => "#{rand(10**30).to_s.rjust(30, '0')}",
'timestamp' => "#{Time.zone.now.to_formatted_s(:iso8601)}",
# 'email' => Setting.registry_email,
'customer_url' => customer_url,
Expand All @@ -78,4 +81,4 @@ def body
'structured_reference' => reference_number.to_s
}
end
end
end
4 changes: 4 additions & 0 deletions app/services/refund_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ def call
contract = RefundContract.new
result = contract.call(amount: amount, payment_reference: payment_reference)

puts '------ REFUND SERVICE ------'
puts result.inspect
puts '------ END REFUND SERVICE ------'

if result.success?
response = base_request
struct_response(response)
Expand Down
50 changes: 50 additions & 0 deletions app/services/void_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
class VoidService
include Request
include ApplicationService

attr_reader :payment_reference

def initialize(payment_reference:)
@payment_reference = payment_reference
end

def self.call(payment_reference:)
new(payment_reference: payment_reference).call
end

def call
contract = VoidContract.new
result = contract.call(payment_reference: payment_reference)

puts '-------- VOID SERVICE --------'
puts result.inspect
puts '-------- END VOID SERVICE --------'

if result.success?
response = base_request
struct_response(response)
else
parse_validation_errors(result)
end
end

private

def base_request
uri = URI("#{GlobalVariable::BASE_ENDPOINT}#{GlobalVariable::VOID_ENDPOINT}")
post(direction: 'everypay', path: uri, params: body)
end

def body
{
'api_username' => GlobalVariable::API_USERNAME,
'payment_reference' => payment_reference,
'nonce' => nonce,
'timestamp' => "#{Time.zone.now.to_formatted_s(:iso8601)}"
}
end

def nonce
rand(10**30).to_s.rjust(30, '0')
end
end
Loading