Skip to content
Draft
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
1 change: 1 addition & 0 deletions lib/payrix.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
require 'payrix/api_operations'
require 'payrix/base_resource'
require 'payrix/resources'
require 'payrix/test_server'

# The Payrix API.
module Payrix
Expand Down
2 changes: 2 additions & 0 deletions lib/payrix/resources/payment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

module Payrix
class Payment
RESOURCE_ENDPOINT = 'payments'

METHOD_AMERICAN_EXPRESS = 1
METHOD_VISA = 2
METHOD_MASTERCARD = 3
Expand Down
102 changes: 102 additions & 0 deletions lib/payrix/resources/txn.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,107 @@ class Txn < BaseResource
COF_TYPE_UNSCHEDULED = 'unscheduled'

extend Payrix::APIOperations::DeleteDisabled

def self.resource_stub
{
id: TestAPI.generate_uuid(Txn),
status: STATUS_APPROVED,
created: '2025-07-28 09:31:10.2165',
modified: '2025-07-29 22:00:24.3132',
creator: TestAPI.generate_uuid(Login),
modifier: '000000000000001',
ip_created: '142.198.14.32',
ip_modified: '142.198.14.32',
merchant: TestAPI.generate_uuid(Merchant),
token: 'fe896cce70b841821915950270f33a94',
payment: TestAPI.generate_uuid(Payment),
fortxn: nil,
fromtxn: nil,
batch: nil,
subscription: nil,
type: 7,
expiration: nil,
currency: 'USD',
platform: 'VANTIV',
auth_date: nil,
auth_code: nil,
captured: '2025-07-28 10:05:05',
settled: 20250728,
settled_currency: 'USD',
settled_total: 100200,
allow_partial: 0,
order: '0003465',
description: nil,
descriptor: 'HM Z',
terminal: nil,
terminal_capability: nil,
entry_mode: nil,
origin: 2,
tax: nil,
total: 100200,
cashback: nil,
authorization: nil,
approved: 100200,
cvv: 0,
swiped: 0,
emv: 0,
signature: 0,
unattended: nil,
client_ip: nil,
first: '',
middle: nil,
last: '',
company: nil,
email: nil,
address1: '123 Main Street',
address2: '',
city: 'New York',
state: 'NH',
zip: '12345',
country: nil,
phone: nil,
refunded: 0,
reserved: 0,
misused: nil,
imported: 0,
inactive: 0,
frozen: 0,
discount: 0,
shipping: 0,
duty: 0,
pin: 0,
trace_number: nil,
cvv_status: nil,
unauth_reason: nil,
fee: 200,
funding_currency: 'USD',
authentication: nil,
authentication_id: nil,
cof_type: nil,
copy_reason: nil,
original_approved: 100200,
currency_conversion: nil,
service_code: nil,
auth_token_customer: nil,
debt_repayment: 0,
statement: nil,
convenience_fee: 0,
surcharge: nil,
channel: nil,
funded: 20250729,
funding_enabled: 1,
request_sequence: 1,
processed_sequence: 1,
mobile: nil,
pin_entry_capability: nil,
returned: nil,
txnsession: nil,
network_token_indicator: 0,
soft_pos_device_type_indicator: nil,
soft_pos_id: nil,
tip: nil,
pinless_debit_conversion: nil
}
end
end
end
8 changes: 8 additions & 0 deletions lib/payrix/test_api/resources/base_resource.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

module Payrix
module TestAPI
class BaseResource
end
end
end
Empty file.
248 changes: 248 additions & 0 deletions lib/payrix/test_server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
# frozen_string_literal: true

require 'webmock'

module Payrix
class TestAPI
KLASS_TO_UUID_MAPPING = {
Login => 'log',
Merchant => 'mer',
OrgEntity => 'oet',
Payment => 'pmt',
Txn => 'txn',
}.freeze

ENDPOINT_TO_KLASS_MAPPING =
KLASS_TO_UUID_MAPPING
.keys
.map { |klass| [klass::RESOURCE_ENDPOINT, klass] }
.to_h
.freeze

def self.generate_uuid(klass)
universal_identifier = KLASS_TO_UUID_MAPPING[klass]
characters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']

"t1_#{universal_identifier}_#{Array.new(23) { characters.sample }.join}"
end

def initialize
@store = Store.new
@history = History.new
@endpoints = ENDPOINT_TO_KLASS_MAPPING.keys

@endpoints.each do |endpoint|
stub_post(endpoint)
stub_delete(endpoint)
stub_get(endpoint)
end
end

def stub_retrieve(endpoint, id, values = {})
check_endpoint!(endpoint)

klass = ENDPOINT_TO_KLASS_MAPPING[endpoint]

@store.add(endpoint, klass.resource_stub.merge(id: id).merge(values))
end

def created(endpoint, filter = {})
check_endpoint!(endpoint)

@history.created(endpoint, filter)
end

def deleted(endpoint, filter = {})
check_endpoint!(endpoint)

@history.deleted(endpoint, filter)
end

private

def check_endpoint!(endpoint)
return if @endpoints.include?(endpoint)

raise ArgumentError, "Endpoint #{endpoint} does not exist."
end

class Request
def initialize(request)
@request = request
end

def endpoint
@request.uri.path.split('/')[1]
end

def klass
ENDPOINT_TO_KLASS_MAPPING[endpoint]
end

def body
JSON.parse(@request.body).transform_keys(&:to_sym)
end

def path
@request.uri.path
end

def search
search = {}

@request
.headers['Search']
.split('&')
.each do |filter|
match = filter.match(/(\w+)\[(\w+)\]=(.+)/)

search[match[1].to_sym] = match[3]
end

search
end
end

class Store
def initialize
@store = Hash.new { |h, k| h[k] = [] }
end

def add(endpoint, resource)
@store[endpoint].push(resource)
end

def remove(endpoint, id)
@store[endpoint].delete(id)
end

def retrieve(endpoint, filter = {})
resources = @store[endpoint]

resources.select do |resource|
filter.all? { |k, v| resource[k].to_s == v }
end
end
end

class History
def initialize
@history = Hash.new { |h, k| h[k] = { created: [], deleted: [] } }
end

def created(endpoint, filter = {})
klass = ENDPOINT_TO_KLASS_MAPPING[endpoint]

created = @history[endpoint][:created]

created.select do |resource|
filter.all? { |k, v| resource[k] == v }
end
end

def deleted(endpoint, filter = {})
klass = ENDPOINT_TO_KLASS_MAPPING[endpoint]

deleted = @history[endpoint][:deleted]

deleted.select do |resource|
filter.all? { |k, v| resource[k] == v }
end
end

def record_creation(endpoint, resource)
@history[endpoint][:created].push(resource)
end

def record_deletion(endpoint, resource)
@history[endpoint][:deleted].push(resource)
end
end

def stub_post(endpoint)
WebMock
.stub_request(:post, %r{^#{Payrix.configuration.url}/#{endpoint}.*$})
.to_return do |request|
request = Request.new(request)

resource = request.klass.resource_stub.merge(request.body)

@store.add(request.endpoint, resource)
@history.record_creation(request.endpoint, resource)

resources = @store.retrieve(request.endpoint, { id: resource[:id] })

{
status: 200,
body: {
response: {
data: resources,
details: {
requestId: 1,
},
errors: [],
},
}.to_json,
}
end
end

def stub_delete(endpoint)
WebMock
.stub_request(:delete, %r{^#{Payrix.configuration.url}/#{endpoint}/.*$})
.to_return do |request|
request = Request.new(request)

id = request.path.split('/').last

resource = @store.remove(request.endpoint, id)

unless resource.nil?
@history.record_deletion(request.endpoint, resource)
end

{
status: 200,
body: {
response: {
data: resource.nil? ? [] : [resource],
details: {
requestId: 1,
},
errors: [],
},
}.to_json,
}
end
end

def stub_get(endpoint)
WebMock
.stub_request(:get, %r{^#{Payrix.configuration.url}/#{endpoint}.*$})
.to_return do |request|
request = Request.new(request)

resources = @store.retrieve(request.endpoint, request.search)

{
status: 200,
body: {
response: {
data: resources,
details: {
requestId: 1,
totals: [],
page: {
current: 1,
last: 1,
hasMore: false,
},
},
errors: [],
},
}.to_json,
}
end
end
end
end
Loading
Loading