Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: Implemented support for iDEAL QR #1

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
2 changes: 1 addition & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Metrics/ModuleLength:
Enabled: false

Metrics/PerceivedComplexity:
Max: 9
Max: 10


Naming/AccessorMethodName:
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,16 @@ response = gateway.status(transaction_id: 12345)

See `Buckaruby::StatusResponse` for more details.

### Generate

TODO: describe generate / iDEAL QR

```
response = gateway.generate(service: Buckaruby::Service::IDEAL_QR, description: "Kentaa", amount: "12.50", purchase_id: "12345", expires_at: Date.today + 30, image_size: 250)
```

See `Buckaruby::DataResponse` for more details.

### Merchant variables

You can send custom variables and additional variables with each request.
Expand Down
1 change: 1 addition & 0 deletions lib/buckaruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
require_relative 'buckaruby/language'
require_relative 'buckaruby/operation'
require_relative 'buckaruby/payment_method'
require_relative 'buckaruby/service'
require_relative 'buckaruby/transaction_status'
require_relative 'buckaruby/transaction_type'

Expand Down
1 change: 1 addition & 0 deletions lib/buckaruby/action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ module Action
PAY_RECURRENT = "PayRecurrent"
REFUND = "Refund"
EXTRA_INFO = "ExtraInfo"
GENERATE = "Generate"
end
end
40 changes: 40 additions & 0 deletions lib/buckaruby/gateway.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ def issuers(payment_method)
Ideal::ISSUERS
end

# New generate data request.
def generate(options = {})
@logger.debug("[generate] options=#{options.inspect}")

validate_generate_params!(options)

execute_request(:data, options)
end

# Setup a new transaction.
def setup_transaction(options = {})
@logger.debug("[setup_transaction] options=#{options.inspect}")
Expand Down Expand Up @@ -154,6 +163,26 @@ def validate_required_params!(params, *required)
end
end

# Validate params for generate request.
def validate_generate_params!(options)
required_params = [:service]

if options[:service] == Service::IDEAL_QR
required_params << [:amount, :description, :purchase_id, :expires_at]
required_params << [:minimum_amount, :maximum_amount] if options.key?(:amount_changeable)
end

validate_required_params!(options, required_params)

if options[:service] == Service::IDEAL_QR
validate_amount!(options)

# TODO: validate minimum/maximum amount
end

validate_service!(options, Service::IDEAL_QR)
end

# Validate params for setup transaction.
def validate_setup_transaction_params!(options)
required_params = [:amount, :payment_method, :invoicenumber]
Expand Down Expand Up @@ -195,6 +224,13 @@ def validate_payment_method!(options, valid)
end
end

# Validate the service.
def validate_service!(options, valid)
unless valid.include?(options[:service])
raise ArgumentError, "Invalid service: #{options[:service]}"
end
end

# Validate the payment issuer when iDEAL is selected as payment method.
def validate_payment_issuer!(options)
if options[:payment_method] == PaymentMethod::IDEAL || options[:payment_method] == PaymentMethod::IDEAL_PROCESSING
Expand Down Expand Up @@ -264,6 +300,8 @@ def execute_request(request_type, options = {})
StatusResponse.new(response, config)
when :cancel
CancelResponse.new(response, config)
when :data
DataResponse.new(response, config)
end
end

Expand All @@ -284,6 +322,8 @@ def build_request(request_type)
StatusRequest.new(config)
when :cancel
CancelRequest.new(config)
when :data
DataRequest.new(config)
end
end
end
Expand Down
7 changes: 4 additions & 3 deletions lib/buckaruby/operation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

module Buckaruby
module Operation
CANCEL_TRANSACTION = "CancelTransaction"
DATA_REQUEST = "DataRequest"
REFUND_INFO = "RefundInfo"
TRANSACTION_REQUEST = "TransactionRequest"
TRANSACTION_STATUS = "TransactionStatus"
TRANSACTION_REQUEST_SPECIFICATION = "TransactionRequestSpecification"
REFUND_INFO = "RefundInfo"
CANCEL_TRANSACTION = "CancelTransaction"
TRANSACTION_STATUS = "TransactionStatus"
end
end
56 changes: 56 additions & 0 deletions lib/buckaruby/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -267,4 +267,60 @@ def build_request_params(options)
params
end
end

# Request for a "Data request".
class DataRequest < Request
def execute(options)
super(options.merge(operation: Operation::DATA_REQUEST))
end

def build_request_params(options)
params = { brq_payment_method: options[:service] }

case options[:service]
when Service::IDEAL_QR
params.merge!(build_idealqr_params(options))
end

params
end

private

def build_idealqr_params(options)
params = {
brq_service_idealqr_action: Action::GENERATE,
brq_service_idealqr_description: options[:description],
brq_service_idealqr_amount: BigDecimal(options[:amount].to_s).to_s("F"),
brq_service_idealqr_purchaseid: options[:purchase_id],
brq_service_idealqr_expiration: options[:expires_at].strftime("%Y-%m-%d")
}

# These parameters are mandatory, but we provide a default value.
if options.key?(:oneoff)
params[:brq_service_idealqr_isoneoff] = options[:oneoff]
else
params[:brq_service_idealqr_isoneoff] = false
end

if options.key?(:amount_changeable)
params[:brq_service_idealqr_amountischangeable] = options[:amount_changeable]
else
params[:brq_service_idealqr_amountischangeable] = false
end

if options.key?(:image_size)
params[:brq_service_idealqr_imagesize] = options[:image_size]
else
params[:brq_service_idealqr_imagesize] = 100
end

# Optional parameters.
params[:brq_service_idealqr_isprocessing] = options[:processing] if options.key?(:processing)
params[:brq_service_idealqr_minamount] = options[:minimum_amount] if options.key?(:minimum_amount)
params[:brq_service_idealqr_maxamount] = options[:maximum_amount] if options.key?(:maximum_amount)

params
end
end
end
17 changes: 17 additions & 0 deletions lib/buckaruby/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -365,4 +365,21 @@ def custom_parameters
@custom_parameters ||= FieldMapper.map_fields(params, :brq_customparameters)
end
end

# Response for a data request.
class DataResponse < ApiResponse
def data_request
params[:brq_datarequest]
end

def service
params[:brq_primary_service].downcase
end

def qr_image_url
if service == Service::IDEAL_QR
params[:brq_service_idealqr_qrimageurl]
end
end
end
end
7 changes: 7 additions & 0 deletions lib/buckaruby/service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module Buckaruby
module Service
IDEAL_QR = "idealqr"
end
end
44 changes: 44 additions & 0 deletions spec/buckaruby/gateway_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -753,4 +753,48 @@
end
end
end

describe '#generate' do
before do
stub_request(:post, "https://testcheckout.buckaroo.nl/nvp/?op=DataRequest").to_return(body: File.read("spec/fixtures/responses/generate_idealqr_success.txt"))
end

it 'raises an exception when parameters are missing' do
expect {
subject.generate
}.to raise_error(ArgumentError)

expect {
subject.generate(service: Buckaruby::Service::IDEAL_QR)
}.to raise_error(ArgumentError)

expect {
subject.generate(service: Buckaruby::Service::IDEAL_QR, amount: 10)
}.to raise_error(ArgumentError)

expect {
subject.generate(service: Buckaruby::Service::IDEAL_QR, amount: 10, description: "test")
}.to raise_error(ArgumentError)

expect {
subject.generate(service: Buckaruby::Service::IDEAL_QR, amount: 10, description: "test", purchase_id: "12345")
}.to raise_error(ArgumentError)
end

it 'raises an exception when initiating a data request with invalid service' do
expect {
subject.generate(service: "abc")
}.to raise_error(ArgumentError)
end

it 'initiates a data request for iDEAL QR' do
response = subject.generate(service: Buckaruby::Service::IDEAL_QR, amount: 10, description: "test", purchase_id: "12345", expires_at: Date.new(2018, 1, 1))
expect(response).to be_an_instance_of(Buckaruby::DataResponse)
expect(response.status).to eq(Buckaruby::TransactionStatus::SUCCESS)
expect(response.timestamp).to be_an_instance_of(Time)
expect(response.data_request).to eq("41C48B55FA9164E123CC73B1157459E840BE5D24")
expect(response.service).to eq(Buckaruby::Service::IDEAL_QR)
expect(response.qr_image_url).to eq("https://qrcode.ideal.nl/qrcode/12345678-1234-1234-1234-123456789012.png")
end
end
end
1 change: 1 addition & 0 deletions spec/fixtures/responses/generate_idealqr_success.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BRQ_APIRESULT=Success&BRQ_DATAREQUEST=41C48B55FA9164E123CC73B1157459E840BE5D24&BRQ_PRIMARY_SERVICE=IdealQr&BRQ_SERVICE_IDEALQR_QRIMAGEURL=https%3A%2F%2Fqrcode.ideal.nl%2Fqrcode%2F12345678-1234-1234-1234-123456789012.png&BRQ_STATUSCODE=190&BRQ_STATUSCODE_DETAIL=S001&BRQ_STATUSMESSAGE=Transaction+successfully+processed&BRQ_TEST=true&BRQ_TIMESTAMP=2018-02-20+13%3A01%3A27&BRQ_WEBSITEKEY=12345678&BRQ_SIGNATURE=9c91f47ecae81fc4d1ab39c8776daacef5bf38b3