docdata-order 1.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 10bb3f962150a565fe9b496efee90734099c09ff
4
- data.tar.gz: 24489d017c6bd41765da9b4baf0bc46b8fff2bd1
2
+ SHA256:
3
+ metadata.gz: 4e9a69ef9b4a6b563b917c5e8b4c1509796f580c285b84dac289a4b7e0d9c6ff
4
+ data.tar.gz: e4280bb171fb3aecf0412f4546f47054504ea986dd1491f0310e5ba87aba769d
5
5
  SHA512:
6
- metadata.gz: 80158fcacd7d4640785952e0f55561f8888e8f92c80926ad3e2e23ab3a4273586eb51017a20133b9828c0b2c73e7ac975d31fde137e0db7100d0ddf5248b6b7b
7
- data.tar.gz: 1539b96288ca0faaefb15a7aecb88cbd26daab80329fde08b1b4c2f653aab9420a529eb32fd43816368315071c44a9f4e5a7219c630867e5c03f1efaf9538a0c
6
+ metadata.gz: 8f1711b410acb0e21d1b2d2cbbe4097806dc08df39e03879d12f34def9bae4ea6290afbadffa02600fd42c1b9056c1dc2721513323f0880ac1b0d561ecb61c4e
7
+ data.tar.gz: b5c6891840ec43f427446bbeaaa43de2d2cf455e438b454eceebab361801338be784c687020eae1675e5d75d3e07fc11ed3562bca122d74c64338712ea0d96fb
@@ -0,0 +1,23 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request:
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ ruby-version: ['2.4', '2.5', '2.6', '2.7', '3.0']
15
+
16
+ steps:
17
+ - uses: actions/checkout@v2
18
+ - uses: ruby/setup-ruby@v1
19
+ with:
20
+ ruby-version: ${{ matrix.ruby-version }}
21
+ bundler-cache: true
22
+ - name: Run tests
23
+ run: bundle exec rake
data/.gitignore CHANGED
@@ -1,6 +1,5 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
- /Gemfile.lock
4
3
  /_yardoc/
5
4
  /coverage/
6
5
  /doc/
data/.rubocop.yml CHANGED
@@ -1,16 +1,26 @@
1
1
  # Docdata-order RuboCop configuration
2
2
 
3
+ require:
4
+ - rubocop-performance
5
+ - rubocop-rake
6
+ - rubocop-rspec
7
+
3
8
  AllCops:
4
- TargetRubyVersion: 2.3
9
+ NewCops: enable
10
+ TargetRubyVersion: 2.4
5
11
  DisplayCopNames: true
6
12
  DisplayStyleGuide: true
13
+ Exclude:
14
+ - 'tmp/**/*'
15
+ - 'vendor/**/*'
7
16
 
8
- # We target 2.3, but still want to support 2.0.
9
- Gemspec/RequiredRubyVersion:
17
+
18
+ Layout/LineLength:
10
19
  Enabled: false
11
20
 
21
+
12
22
  Metrics/AbcSize:
13
- Max: 53
23
+ Max: 58
14
24
 
15
25
  Metrics/BlockLength:
16
26
  Exclude:
@@ -19,25 +29,27 @@ Metrics/BlockLength:
19
29
  Metrics/ClassLength:
20
30
  Enabled: false
21
31
 
22
- Metrics/LineLength:
23
- Enabled: false
24
-
25
32
  Metrics/MethodLength:
26
33
  Enabled: false
27
34
 
35
+
36
+ RSpec/DescribedClass:
37
+ EnforcedStyle: explicit
38
+
39
+ RSpec/ExampleLength:
40
+ Max: 41
41
+
42
+ RSpec/MultipleExpectations:
43
+ Max: 10
44
+
45
+
28
46
  Style/BlockDelimiters:
29
47
  Exclude:
30
48
  - 'spec/**/*.rb'
31
49
 
32
- Style/ExpandPathArguments:
33
- Enabled: false
34
-
35
50
  Style/GuardClause:
36
51
  Enabled: false
37
52
 
38
- Style/SafeNavigation:
39
- Enabled: false
40
-
41
53
  Style/StringLiterals:
42
54
  Enabled: false
43
55
 
data/Gemfile CHANGED
@@ -5,4 +5,7 @@ source 'https://rubygems.org'
5
5
  # Specify your gem's dependencies in docdata-order.gemspec
6
6
  gemspec
7
7
 
8
- gem 'rubocop', '~> 0.59.2'
8
+ gem 'rubocop', '~> 1.12.0'
9
+ gem 'rubocop-performance', '~> 1.10.2'
10
+ gem 'rubocop-rake', '~> 0.5.1'
11
+ gem 'rubocop-rspec', '~> 2.2.0'
data/Gemfile.lock ADDED
@@ -0,0 +1,106 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ docdata-order (2.1.0)
5
+ savon (>= 2.0, < 3.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ addressable (2.8.0)
11
+ public_suffix (>= 2.0.2, < 5.0)
12
+ akami (1.3.1)
13
+ gyoku (>= 0.4.0)
14
+ nokogiri
15
+ ast (2.4.2)
16
+ builder (3.2.4)
17
+ crack (0.4.5)
18
+ rexml
19
+ diff-lcs (1.4.4)
20
+ gyoku (1.3.1)
21
+ builder (>= 2.1.2)
22
+ hashdiff (1.0.1)
23
+ httpi (2.4.5)
24
+ rack
25
+ socksify
26
+ mini_portile2 (2.4.0)
27
+ nokogiri (1.10.10)
28
+ mini_portile2 (~> 2.4.0)
29
+ nori (2.6.0)
30
+ parallel (1.20.1)
31
+ parser (3.0.2.0)
32
+ ast (~> 2.4.1)
33
+ public_suffix (4.0.6)
34
+ rack (2.2.3)
35
+ rainbow (3.0.0)
36
+ rake (13.0.6)
37
+ regexp_parser (2.1.1)
38
+ rexml (3.2.5)
39
+ rspec (3.10.0)
40
+ rspec-core (~> 3.10.0)
41
+ rspec-expectations (~> 3.10.0)
42
+ rspec-mocks (~> 3.10.0)
43
+ rspec-core (3.10.1)
44
+ rspec-support (~> 3.10.0)
45
+ rspec-expectations (3.10.1)
46
+ diff-lcs (>= 1.2.0, < 2.0)
47
+ rspec-support (~> 3.10.0)
48
+ rspec-mocks (3.10.2)
49
+ diff-lcs (>= 1.2.0, < 2.0)
50
+ rspec-support (~> 3.10.0)
51
+ rspec-support (3.10.2)
52
+ rubocop (1.12.1)
53
+ parallel (~> 1.10)
54
+ parser (>= 3.0.0.0)
55
+ rainbow (>= 2.2.2, < 4.0)
56
+ regexp_parser (>= 1.8, < 3.0)
57
+ rexml
58
+ rubocop-ast (>= 1.2.0, < 2.0)
59
+ ruby-progressbar (~> 1.7)
60
+ unicode-display_width (>= 1.4.0, < 3.0)
61
+ rubocop-ast (1.4.1)
62
+ parser (>= 2.7.1.5)
63
+ rubocop-performance (1.10.2)
64
+ rubocop (>= 0.90.0, < 2.0)
65
+ rubocop-ast (>= 0.4.0)
66
+ rubocop-rake (0.5.1)
67
+ rubocop
68
+ rubocop-rspec (2.2.0)
69
+ rubocop (~> 1.0)
70
+ rubocop-ast (>= 1.1.0)
71
+ ruby-progressbar (1.11.0)
72
+ savon (2.12.1)
73
+ akami (~> 1.2)
74
+ builder (>= 2.1.2)
75
+ gyoku (~> 1.2)
76
+ httpi (~> 2.3)
77
+ nokogiri (>= 1.8.1)
78
+ nori (~> 2.4)
79
+ wasabi (~> 3.4)
80
+ socksify (1.7.1)
81
+ unicode-display_width (2.0.0)
82
+ wasabi (3.6.1)
83
+ addressable
84
+ httpi (~> 2.0)
85
+ nokogiri (>= 1.4.2)
86
+ webmock (2.3.2)
87
+ addressable (>= 2.3.6)
88
+ crack (>= 0.3.2)
89
+ hashdiff
90
+
91
+ PLATFORMS
92
+ ruby
93
+
94
+ DEPENDENCIES
95
+ bundler (~> 2.0)
96
+ docdata-order!
97
+ rake (~> 13.0)
98
+ rspec (~> 3.0)
99
+ rubocop (~> 1.12.0)
100
+ rubocop-performance (~> 1.10.2)
101
+ rubocop-rake (~> 0.5.1)
102
+ rubocop-rspec (~> 2.2.0)
103
+ webmock (~> 2.3, >= 2.3.2)
104
+
105
+ BUNDLED WITH
106
+ 2.2.17
data/README.md CHANGED
@@ -1,11 +1,30 @@
1
1
  # Docdata::Order
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/docdata-order.svg)](https://badge.fury.io/rb/docdata-order)
4
- [![Build Status](https://travis-ci.org/KentaaNL/docdata-order.svg?branch=master)](https://travis-ci.org/KentaaNL/docdata-order)
4
+ [![Build Status](https://github.com/KentaaNL/docdata-order/actions/workflows/test.yml/badge.svg)](https://github.com/KentaaNL/docdata-order/actions)
5
5
  [![Code Climate](https://codeclimate.com/github/KentaaNL/docdata-order/badges/gpa.svg)](https://codeclimate.com/github/KentaaNL/docdata-order)
6
6
 
7
7
  Docdata::Order is a Ruby client for the Docdata Order API version 1.3.
8
8
 
9
+ ## Table of Contents
10
+
11
+ - [Installation](#installation)
12
+ - [Usage](#usage)
13
+ - [Initialization](#initialization)
14
+ - [Subject merchant](#subject-merchant)
15
+ - [Create an order](#create-an-order)
16
+ - [Redirecting directly to the payment page](#redirecting-directly-to-the-payment-page)
17
+ - [Initial payment request](#initial-payment-request)
18
+ - [Start a payment order](#start-a-payment-order)
19
+ - [Recurring payment request](#recurring-payment-request)
20
+ - [Retrieve status of an order](#retrieve-status-of-an-order)
21
+ - [Retrieve payment methods](#retrieve-payment-methods)
22
+ - [Refund a payment](#refund-a-payment)
23
+ - [Development](#development)
24
+ - [Contributing](#contributing)
25
+ - [License](#license)
26
+
27
+
9
28
  ## Installation
10
29
 
11
30
  Add this line to your application's Gemfile:
@@ -29,14 +48,25 @@ Or install it yourself as:
29
48
  Create a Docdata Order client and configure it with your merchant name and password:
30
49
 
31
50
  ```ruby
32
- order = Docdata::Order::Client.new("name", "password")
51
+ client = Docdata::Order::Client.new("name", "password")
33
52
  ```
34
53
 
35
- The client is configured in live mode by default. To put the client in test mode, use `test: true` as third parameter.
54
+ The client is configured to use the production environment by default. To use the Docdata test environment, add the parameter `test: true` when creating the client.
55
+
56
+ #### Subject merchant
57
+
58
+ If you want to use a merchant on whose behalf the requests should be executed (subject merchant), then you can add this to the parameters:
59
+
60
+ ```ruby
61
+ client = Docdata::Order::Client.new("name", "password", subject_merchant: {
62
+ name: "subname",
63
+ token: "12345678"
64
+ })
65
+ ```
36
66
 
37
67
  ### Create an order
38
68
 
39
- Create a new order with the `create` method. You need to provide the following parameters to create the order:
69
+ Create a new order with the `create` method. You need to provide at least the following parameters to create the order:
40
70
 
41
71
  ```ruby
42
72
  options = {
@@ -60,7 +90,7 @@ options = {
60
90
  }
61
91
  }
62
92
 
63
- response = order.create(options)
93
+ response = client.create(options)
64
94
 
65
95
  if response.success?
66
96
  puts response.order_key
@@ -72,29 +102,47 @@ end
72
102
 
73
103
  The `redirect_url` in the response will redirect the user to the Docdata Payment Menu (One Page Checkout).
74
104
 
75
- To create an order and automatically redirect to the bank using iDEAL as payment method, use the parameters above including:
105
+ #### Redirecting directly to the payment page
106
+
107
+ For some payment methods you can skip the Docdata One Page Checkout and redirect directly to the payment page of the specified payment method.
108
+ This works for the payment methods iDEAL, Sofort and PayPal. For iDEAL, you also need to provide the issuer:
76
109
 
77
110
  ```ruby
78
- response = order.create(options.merge(
111
+ response = client.create(options.merge(
79
112
  payment_method: Docdata::Order::PaymentMethod::IDEAL,
80
- issuer_id: Docdata::Order::Ideal::ISSUERS.first[0],
113
+ issuer_id: "INGBNL2A",
81
114
  return_url: "http://yourwebshop.nl/payment_return"
82
115
  ))
83
116
  ```
84
117
 
118
+ #### Initial payment request
119
+
120
+ If you want to use this order to do recurring payments then you must first make an initial payment request and provide an unique merchant reference.
121
+ This reference should then be used when a making recurring payments.
122
+
123
+ ```ruby
124
+ response = client.create(options.merge(
125
+ initial: {
126
+ merchant_reference: "12345"
127
+ }
128
+ ))
129
+ ```
130
+
131
+ See [Recurring payment request](#recurring-payment-request) how to make recurring payment requests.
132
+
85
133
  ### Start a payment order
86
134
 
87
- When using Webdirect, you can use `start` to start a payment order.
135
+ When the One Page Checkout is not used (i.e. WebDirect) then you need to use `start` to start a payment order.
88
136
 
89
137
  ```ruby
90
138
  options = {
91
139
  order_key: "12345",
92
140
  payment_method: Docdata::Order::PaymentMethod::SEPA_DIRECT_DEBIT,
93
- account_name: "Onderheuvel",
94
- account_iban: "NL44RABO0123456789"
141
+ consumer_name: "Onderheuvel",
142
+ consumer_iban: "NL44RABO0123456789"
95
143
  }
96
144
 
97
- response = order.start(options)
145
+ response = client.start(options)
98
146
 
99
147
  if response.success?
100
148
  puts response.payment_id
@@ -103,12 +151,24 @@ else
103
151
  end
104
152
  ```
105
153
 
154
+ #### Recurring payment request
155
+
156
+ To start a recurring payment request, you need to provide the unique merchant reference, used in the initial payment request:
157
+
158
+ ```ruby
159
+ response = client.start(options.merge(
160
+ recurring: {
161
+ merchant_reference: "12345"
162
+ }
163
+ ))
164
+ ```
165
+
106
166
  ### Retrieve status of an order
107
167
 
108
168
  To retrieve the status of an order, use `status` with the order key:
109
169
 
110
170
  ```ruby
111
- response = order.status(order_key: "12345")
171
+ response = client.status(order_key: "12345")
112
172
 
113
173
  if response.success?
114
174
  puts response.paid?
@@ -117,6 +177,33 @@ else
117
177
  end
118
178
  ```
119
179
 
180
+ ### Retrieve payment methods
181
+
182
+ When an order has been created, you can retrieve the available payment methods (including issuers) by using `payment_methods` with the order key:
183
+
184
+ ```ruby
185
+ response = client.payment_methods(order_key: "12345")
186
+
187
+ if response.success?
188
+ puts response.payment_methods
189
+ else
190
+ puts response.error_message
191
+ end
192
+ ```
193
+
194
+ ### Refund a payment
195
+
196
+ To refund a payment, use `refund` with the payment ID:
197
+
198
+ ```ruby
199
+ response = client.refund(payment_id: "12345")
200
+
201
+ if !response.success?
202
+ puts response.error_message
203
+ end
204
+
205
+ ```
206
+
120
207
  ## Development
121
208
 
122
209
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('../lib', __FILE__)
3
+ lib = File.expand_path('lib', __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require 'docdata/order/version'
6
6
 
@@ -21,10 +21,10 @@ Gem::Specification.new do |spec|
21
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
22
  spec.require_paths = ["lib"]
23
23
 
24
- spec.required_ruby_version = ">= 2.0.0"
24
+ spec.required_ruby_version = ">= 2.4.0"
25
25
 
26
- spec.add_development_dependency "bundler", "~> 1.14"
27
- spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "bundler", "~> 2.0"
27
+ spec.add_development_dependency "rake", "~> 13.0"
28
28
  spec.add_development_dependency "rspec", "~> 3.0"
29
29
  spec.add_development_dependency "webmock", "~> 2.3", ">= 2.3.2"
30
30
 
data/lib/docdata/order.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "docdata/order/amount"
3
4
  require "docdata/order/client"
4
5
  require "docdata/order/exception"
5
6
  require "docdata/order/gender"
6
- require "docdata/order/ideal"
7
7
  require "docdata/order/payment_method"
8
8
  require "docdata/order/request"
9
9
  require "docdata/order/response"
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bigdecimal'
4
+
5
+ module Docdata
6
+ module Order
7
+ # Helper for converting/formatting amounts.
8
+ class Amount
9
+ class << self
10
+ def from_cents(cents)
11
+ new(cents.to_i / 100.0)
12
+ end
13
+ end
14
+
15
+ def initialize(amount)
16
+ @amount = BigDecimal(amount.to_s)
17
+ end
18
+
19
+ def to_d
20
+ @amount
21
+ end
22
+
23
+ def to_amount
24
+ @amount / 100.0
25
+ end
26
+
27
+ def to_cents
28
+ cents = @amount * 100
29
+ cents.to_i
30
+ end
31
+
32
+ # Convert the amount to a String with 2 decimals.
33
+ def to_s
34
+ format("%.2f", @amount)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -18,7 +18,7 @@ module Docdata
18
18
 
19
19
  response = client.call(:create, message: CreateRequest.new(params), attributes: { xmlns: XMLNS_DDP, version: DDP_VERSION })
20
20
 
21
- raise Exception, response unless response.success?
21
+ raise Docdata::Order::Exception, response unless response.success?
22
22
 
23
23
  CreateResponse.new(params, response)
24
24
  end
@@ -28,7 +28,7 @@ module Docdata
28
28
 
29
29
  response = client.call(:start, message: StartRequest.new(params), attributes: { xmlns: XMLNS_DDP, version: DDP_VERSION })
30
30
 
31
- raise Exception, response unless response.success?
31
+ raise Docdata::Order::Exception, response unless response.success?
32
32
 
33
33
  StartResponse.new(params, response)
34
34
  end
@@ -38,20 +38,38 @@ module Docdata
38
38
 
39
39
  response = client.call(:status_extended, message: ExtendedStatusRequest.new(params), attributes: { xmlns: XMLNS_DDP, version: DDP_VERSION })
40
40
 
41
- raise Exception, response unless response.success?
41
+ raise Docdata::Order::Exception, response unless response.success?
42
42
 
43
43
  ExtendedStatusResponse.new(params, response)
44
44
  end
45
45
 
46
+ def refund(options = {})
47
+ params = @options.merge(options)
48
+
49
+ response = client.call(:refund, message: RefundRequest.new(params), attributes: { xmlns: XMLNS_DDP, version: DDP_VERSION })
50
+
51
+ raise Docdata::Order::Exception, response unless response.success?
52
+
53
+ RefundResponse.new(params, response)
54
+ end
55
+
56
+ def payment_methods(options = {})
57
+ params = @options.merge(options)
58
+
59
+ response = client.call(:list_payment_methods, message: ListPaymentMethodsRequest.new(params), attributes: { xmlns: XMLNS_DDP, version: DDP_VERSION })
60
+
61
+ raise Docdata::Order::Exception, response unless response.success?
62
+
63
+ ListPaymentMethodsResponse.new(params, response)
64
+ end
65
+
46
66
  private
47
67
 
48
68
  def client
49
69
  @client ||= begin
50
70
  params = { wsdl: wsdl_url, raise_errors: false, namespace_identifier: nil, namespaces: { "xmlns:ddp" => XMLNS_DDP } }
51
71
 
52
- if @options[:debug]
53
- params.merge!(log: true, log_level: :debug, pretty_print_xml: true)
54
- end
72
+ params.merge!(log: true, log_level: :debug, pretty_print_xml: true) if @options[:debug]
55
73
 
56
74
  params[:logger] = Rails.logger if defined?(Rails)
57
75
 
@@ -2,13 +2,28 @@
2
2
 
3
3
  module Docdata
4
4
  module Order
5
- module PaymentMethod
5
+ # Payment method in Docdata, optionally with issuers.
6
+ class PaymentMethod
6
7
  IDEAL = "IDEAL"
7
8
  VISA = "VISA"
8
9
  MASTER_CARD = "MASTERCARD"
10
+ MAESTRO = "MAESTRO"
9
11
  AMERICAN_EXPRESS = "AMEX"
10
12
  PAYPAL = "PAYPAL_EXPRESS_CHECKOUT"
11
13
  SEPA_DIRECT_DEBIT = "SEPA_DIRECT_DEBIT"
14
+ BANCONTACT = "MISTERCASH"
15
+ SOFORT = "EBANKING"
16
+ GIROPAY = "GIROPAY"
17
+
18
+ attr_accessor :payment_method, :issuers
19
+
20
+ def initialize(payment_method)
21
+ @payment_method = payment_method
22
+ end
23
+
24
+ def to_s
25
+ payment_method
26
+ end
12
27
  end
13
28
  end
14
29
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "builder/xmlmarkup"
4
- require "bigdecimal"
5
4
  require "securerandom"
6
5
 
7
6
  module Docdata
@@ -18,7 +17,22 @@ module Docdata
18
17
  builder = Builder::XmlMarkup.new
19
18
 
20
19
  # Merchant credentials.
21
- builder.merchant(name: merchant_name, password: merchant_password)
20
+ if subject_merchant
21
+ builder.merchant(name: merchant_name, password: merchant_password) do |merchant|
22
+ # The merchant on whose behalf this request should be executed.
23
+ merchant.subjectMerchant(name: subject_merchant_name, token: subject_merchant_token) do |subject|
24
+ if subject_merchant_fee
25
+ # The fee to apply to the subject merchant. If the fee is zero, then it is ignored. A fee can only be applied to create-order requests.
26
+ subject.fee(moment: subject_merchant_fee_moment) do |fee|
27
+ fee.amount(subject_merchant_fee_amount, currency: subject_merchant_fee_currency)
28
+ fee.description(subject_merchant_fee_description) if subject_merchant_fee_description
29
+ end
30
+ end
31
+ end
32
+ end
33
+ else
34
+ builder.merchant(name: merchant_name, password: merchant_password)
35
+ end
22
36
 
23
37
  build_request(builder)
24
38
 
@@ -57,6 +71,38 @@ module Docdata
57
71
  options.fetch(:merchant).fetch(:password)
58
72
  end
59
73
 
74
+ def subject_merchant
75
+ options[:subject_merchant]
76
+ end
77
+
78
+ def subject_merchant_name
79
+ subject_merchant.fetch(:name)
80
+ end
81
+
82
+ def subject_merchant_token
83
+ subject_merchant.fetch(:token)
84
+ end
85
+
86
+ def subject_merchant_fee
87
+ subject_merchant[:fee]
88
+ end
89
+
90
+ def subject_merchant_fee_moment
91
+ subject_merchant_fee[:moment] || "FULLY_PAID"
92
+ end
93
+
94
+ def subject_merchant_fee_description
95
+ subject_merchant_fee[:description]
96
+ end
97
+
98
+ def subject_merchant_fee_amount
99
+ Amount.new(subject_merchant_fee.fetch(:amount)).to_cents
100
+ end
101
+
102
+ def subject_merchant_fee_currency
103
+ subject_merchant_fee[:currency] || "EUR"
104
+ end
105
+
60
106
  def build_request
61
107
  raise NotImplementedError
62
108
  end
@@ -130,14 +176,21 @@ module Docdata
130
176
 
131
177
  # The description that is used by payment providers on shopper statements.
132
178
  builder.receiptText(receipt_text)
179
+
180
+ # The merchant_reference is used for recurring payments.
181
+ if initial
182
+ builder.paymentRequest do |payment_request|
183
+ payment_request.initialPaymentReference do |payment_reference|
184
+ payment_reference.merchantReference(merchant_reference)
185
+ end
186
+ end
187
+ end
133
188
  end
134
189
 
135
190
  private
136
191
 
137
192
  def amount
138
- decimal = BigDecimal(options.fetch(:amount).to_s)
139
- decimal *= 100 # to cents
140
- decimal.to_i
193
+ Amount.new(options.fetch(:amount)).to_cents
141
194
  end
142
195
 
143
196
  def order_reference
@@ -211,6 +264,14 @@ module Docdata
211
264
  def receipt_text
212
265
  options.fetch(:description)[0, 49]
213
266
  end
267
+
268
+ def initial
269
+ options[:initial]
270
+ end
271
+
272
+ def merchant_reference
273
+ initial[:merchant_reference]
274
+ end
214
275
  end
215
276
 
216
277
  # Start a payment order (Webdirect).
@@ -219,22 +280,30 @@ module Docdata
219
280
  # Payment order key belonging to the order for which a transaction needs to be started.
220
281
  builder.paymentOrderKey(order_key)
221
282
 
222
- builder.payment do |payment|
223
- payment.paymentMethod(payment_method)
224
-
225
- case payment_method
226
- when PaymentMethod::IDEAL
227
- payment.iDealPaymentInput do |input|
228
- input.issuerId(issuer_id)
283
+ if recurring
284
+ builder.recurringPaymentRequest do |payment_request|
285
+ payment_request.initialPaymentReference do |payment_reference|
286
+ payment_reference.merchantReference(merchant_reference)
229
287
  end
230
- when PaymentMethod::SEPA_DIRECT_DEBIT
231
- payment.directDebitPaymentInput do |input|
232
- input.holderName(account_name)
233
- input.iban(account_iban)
234
- input.bic(account_bic) if account_bic
288
+ end
289
+ else
290
+ builder.payment do |payment|
291
+ payment.paymentMethod(payment_method)
292
+
293
+ case payment_method
294
+ when PaymentMethod::IDEAL
295
+ payment.iDealPaymentInput do |input|
296
+ input.issuerId(issuer_id)
297
+ end
298
+ when PaymentMethod::SEPA_DIRECT_DEBIT
299
+ payment.directDebitPaymentInput do |input|
300
+ input.holderName(consumer_name)
301
+ input.iban(consumer_iban)
302
+ input.bic(consumer_bic) if consumer_bic
303
+ end
304
+ else
305
+ raise ArgumentError, "Payment method not supported: #{payment_method}"
235
306
  end
236
- else
237
- raise ArgumentError, "Payment method not supported: #{payment_method}"
238
307
  end
239
308
  end
240
309
  end
@@ -246,23 +315,31 @@ module Docdata
246
315
  end
247
316
 
248
317
  def payment_method
249
- options.fetch(:payment_method)
318
+ options.fetch(:payment_method).to_s
250
319
  end
251
320
 
252
321
  def issuer_id
253
322
  options.fetch(:issuer_id)
254
323
  end
255
324
 
256
- def account_name
257
- options.fetch(:account_name)
325
+ def consumer_name
326
+ options.fetch(:consumer_name)
327
+ end
328
+
329
+ def consumer_iban
330
+ options.fetch(:consumer_iban)
331
+ end
332
+
333
+ def consumer_bic
334
+ options[:consumer_bic]
258
335
  end
259
336
 
260
- def account_iban
261
- options.fetch(:account_iban)
337
+ def recurring
338
+ options[:recurring]
262
339
  end
263
340
 
264
- def account_bic
265
- options[:account_bic]
341
+ def merchant_reference
342
+ recurring[:merchant_reference]
266
343
  end
267
344
  end
268
345
 
@@ -278,5 +355,57 @@ module Docdata
278
355
  options.fetch(:order_key)
279
356
  end
280
357
  end
358
+
359
+ # Create a refund request in the DocData system.
360
+ class RefundRequest < Request
361
+ def build_request(builder)
362
+ # The payment ID on which the refund request needs to be performed.
363
+ builder.paymentId(payment_id)
364
+
365
+ # Merchant's internal ID for identifying this refund.
366
+ builder.merchantRefundReference(refund_reference) if refund_reference
367
+
368
+ # Optional amount to refund.
369
+ builder.amount(amount, currency: currency) if amount
370
+
371
+ # Optional description for this refund.
372
+ builder.description(description) if description
373
+ end
374
+
375
+ private
376
+
377
+ def payment_id
378
+ options.fetch(:payment_id)
379
+ end
380
+
381
+ def refund_reference
382
+ options[:refund_reference]
383
+ end
384
+
385
+ def amount
386
+ Amount.new(options[:amount]).to_cents if options[:amount]
387
+ end
388
+
389
+ def currency
390
+ options[:currency] || "EUR"
391
+ end
392
+
393
+ def description
394
+ options[:description]
395
+ end
396
+ end
397
+
398
+ # Retrieve available payment methods for an Order.
399
+ class ListPaymentMethodsRequest < Request
400
+ def build_request(builder)
401
+ builder.paymentOrderKey(order_key)
402
+ end
403
+
404
+ private
405
+
406
+ def order_key
407
+ options.fetch(:order_key)
408
+ end
409
+ end
281
410
  end
282
411
  end
@@ -63,7 +63,7 @@ module Docdata
63
63
  when PaymentMethod::IDEAL
64
64
  params[:default_act] = true
65
65
  params[:ideal_issuer_id] = issuer_id if issuer_id
66
- when PaymentMethod::PAYPAL
66
+ when PaymentMethod::PAYPAL, PaymentMethod::SOFORT
67
67
  params[:default_act] = true
68
68
  end
69
69
  end
@@ -93,7 +93,12 @@ module Docdata
93
93
  end
94
94
 
95
95
  def merchant_name
96
- options.fetch(:merchant).fetch(:name)
96
+ # Use subject merchant when present, otherwise fallback to merchant.
97
+ if options[:subject_merchant]
98
+ options.fetch(:subject_merchant).fetch(:name)
99
+ else
100
+ options.fetch(:merchant).fetch(:name)
101
+ end
97
102
  end
98
103
 
99
104
  def client_language
@@ -101,7 +106,7 @@ module Docdata
101
106
  end
102
107
 
103
108
  def payment_method
104
- options[:payment_method]
109
+ options[:payment_method].to_s
105
110
  end
106
111
 
107
112
  def issuer_id
@@ -280,7 +285,7 @@ module Docdata
280
285
  authorization_status == "CANCELED"
281
286
  end
282
287
 
283
- def account_iban
288
+ def consumer_iban
284
289
  case payment_method
285
290
  when PaymentMethod::IDEAL
286
291
  payment_info = payment[:extended][:i_deal_payment_info]
@@ -291,7 +296,7 @@ module Docdata
291
296
  end
292
297
  end
293
298
 
294
- def account_bic
299
+ def consumer_bic
295
300
  case payment_method
296
301
  when PaymentMethod::IDEAL
297
302
  payment_info = payment[:extended][:i_deal_payment_info]
@@ -302,7 +307,7 @@ module Docdata
302
307
  end
303
308
  end
304
309
 
305
- def account_name
310
+ def consumer_name
306
311
  if payment_method == PaymentMethod::IDEAL
307
312
  payment_info = payment[:extended][:i_deal_payment_info]
308
313
  payment_info[:holder_name] if payment_info
@@ -319,9 +324,53 @@ module Docdata
319
324
  private
320
325
 
321
326
  def to_decimal(cents)
322
- total = BigDecimal(cents)
323
- total /= 100.0
324
- total
327
+ Amount.from_cents(cents).to_d
328
+ end
329
+ end
330
+
331
+ # Response to a refund operation.
332
+ class RefundResponse < Response
333
+ def data
334
+ body[:refund_response]
335
+ end
336
+
337
+ def success?
338
+ data.key?(:refund_success)
339
+ end
340
+
341
+ def error?
342
+ data.key?(:refund_errors)
343
+ end
344
+
345
+ def errors
346
+ data[:refund_errors]
347
+ end
348
+ end
349
+
350
+ # Response to a list payment methods operation.
351
+ class ListPaymentMethodsResponse < Response
352
+ def data
353
+ body[:list_payment_methods_response]
354
+ end
355
+
356
+ def success?
357
+ data.key?(:list_payment_methods_success)
358
+ end
359
+
360
+ def error?
361
+ data.key?(:list_payment_methods_errors)
362
+ end
363
+
364
+ def errors
365
+ data[:list_payment_methods_errors]
366
+ end
367
+
368
+ def payment_methods
369
+ data[:list_payment_methods_success][:payment_method].map do |payment_method|
370
+ method = PaymentMethod.new(payment_method[:name])
371
+ method.issuers = payment_method[:issuers][:issuer].map { |issuer| [issuer.attributes["id"], issuer.to_s] }.to_h if payment_method.key?(:issuers)
372
+ method
373
+ end
325
374
  end
326
375
  end
327
376
  end
@@ -5,11 +5,11 @@ module Docdata
5
5
  module Urls
6
6
  # WSDL location, see Order API 1.3.
7
7
  WSDL_LIVE_URL = "https://secure.docdatapayments.com/ps/services/paymentservice/1_3?wsdl"
8
- WSDL_TEST_URL = "https://test.docdatapayments.com/ps/services/paymentservice/1_3?wsdl"
8
+ WSDL_TEST_URL = "https://testsecure.docdatapayments.com/ps/services/paymentservice/1_3?wsdl"
9
9
 
10
10
  # Payment Menu base URL, see Functional API.
11
11
  MENU_LIVE_URL = "https://secure.docdatapayments.com/ps/menu"
12
- MENU_TEST_URL = "https://test.docdatapayments.com/ps/menu"
12
+ MENU_TEST_URL = "https://testsecure.docdatapayments.com/ps/menu"
13
13
  end
14
14
  end
15
15
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Docdata
4
4
  module Order
5
- VERSION = "1.0.1"
5
+ VERSION = "2.1.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docdata-order
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kentaa
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-10-05 00:00:00.000000000 Z
11
+ date: 2021-07-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.14'
19
+ version: '2.0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.14'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '13.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '13.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -92,19 +92,19 @@ dependencies:
92
92
  - - "<"
93
93
  - !ruby/object:Gem::Version
94
94
  version: '3.0'
95
- description:
95
+ description:
96
96
  email:
97
97
  - support@kentaa.nl
98
98
  executables: []
99
99
  extensions: []
100
100
  extra_rdoc_files: []
101
101
  files:
102
+ - ".github/workflows/test.yml"
102
103
  - ".gitignore"
103
104
  - ".rspec"
104
105
  - ".rubocop.yml"
105
- - ".travis.yml"
106
- - CHANGELOG.md
107
106
  - Gemfile
107
+ - Gemfile.lock
108
108
  - LICENSE.txt
109
109
  - README.md
110
110
  - Rakefile
@@ -112,10 +112,10 @@ files:
112
112
  - bin/setup
113
113
  - docdata-order.gemspec
114
114
  - lib/docdata/order.rb
115
+ - lib/docdata/order/amount.rb
115
116
  - lib/docdata/order/client.rb
116
117
  - lib/docdata/order/exception.rb
117
118
  - lib/docdata/order/gender.rb
118
- - lib/docdata/order/ideal.rb
119
119
  - lib/docdata/order/payment_method.rb
120
120
  - lib/docdata/order/request.rb
121
121
  - lib/docdata/order/response.rb
@@ -125,7 +125,7 @@ homepage: https://github.com/KentaaNL/docdata-order
125
125
  licenses:
126
126
  - MIT
127
127
  metadata: {}
128
- post_install_message:
128
+ post_install_message:
129
129
  rdoc_options: []
130
130
  require_paths:
131
131
  - lib
@@ -133,16 +133,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
133
133
  requirements:
134
134
  - - ">="
135
135
  - !ruby/object:Gem::Version
136
- version: 2.0.0
136
+ version: 2.4.0
137
137
  required_rubygems_version: !ruby/object:Gem::Requirement
138
138
  requirements:
139
139
  - - ">="
140
140
  - !ruby/object:Gem::Version
141
141
  version: '0'
142
142
  requirements: []
143
- rubyforge_project:
144
- rubygems_version: 2.6.14
145
- signing_key:
143
+ rubygems_version: 3.0.8
144
+ signing_key:
146
145
  specification_version: 4
147
146
  summary: Ruby client for the Docdata Order API
148
147
  test_files: []
data/.travis.yml DELETED
@@ -1,7 +0,0 @@
1
- language: ruby
2
- sudo: false
3
- cache: bundler
4
- rvm:
5
- - 2.2.9
6
- - 2.3.6
7
- - 2.4.3
data/CHANGELOG.md DELETED
@@ -1,10 +0,0 @@
1
- # Docdata::Order changelog
2
-
3
- ## 1.0.1 (2018-10-05)
4
-
5
- - Added Moneyou to the list of iDEAL issuers.
6
- - Fixed result of ExtendedStatusResponse#total_reversed.
7
-
8
- ## 1.0.0 (2018-02-09)
9
-
10
- - First public release.
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Docdata
4
- module Order
5
- module Ideal
6
- ISSUERS = {
7
- "ABNANL2A" => "ABN AMRO",
8
- "ASNBNL21" => "ASN Bank",
9
- "BUNQNL2A" => "bunq",
10
- "INGBNL2A" => "ING",
11
- "KNABNL2H" => "Knab bank",
12
- "MOYONL21" => "Moneyou",
13
- "RABONL2U" => "Rabobank",
14
- "RBRBNL21" => "RegioBank",
15
- "SNSBNL2A" => "SNS Bank",
16
- "TRIONL2U" => "Triodos Bank",
17
- "FVLBNL22" => "Van Lanschot"
18
- }.freeze
19
- end
20
- end
21
- end