docdata-order 1.0.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e5eddbd34424c4c03f721739aa30431b62c52b08b3c7a68fdbbbccae0c7a8314
4
- data.tar.gz: d9bd52b4cab16bc451a7c0d57ad3cabd50943cd92c4e9f0ab788271fa317392a
3
+ metadata.gz: 42f2292f2551aa859832a6912c3f3902e7ff7f1712444416e4e3790a41a8fa5e
4
+ data.tar.gz: acaa5f6b5605d628040d448370d861b6a14b750012abfa5ab20df47938eacc7b
5
5
  SHA512:
6
- metadata.gz: d5f6dd825c2249194c7b8466abe5bdd8cc91888f28a4c1e1e8670f99775f7fb919e6f6bdbaac185da10faebe56a387235f4e91933739d0ecf613955d5192812a
7
- data.tar.gz: a33399343e7b6cac2ada00721f4407d35bd6d95ead5d6d7b49acaccd1caca19e37a40213594ceec58cc30bdc7459944797325d2833fed7d12ec409dfde2ed218
6
+ metadata.gz: a61186b4caa42c8353bbafb01293855ac6227de46956678391b8190b4bfb4b4c144cc6ca68eb43a93824eb70b03fd9b3a24e9791ee838411a65bb33c771a2e5c
7
+ data.tar.gz: e8cb189d0bce6274e3717fb02d0008174b7c973c857b96d3f8031aa1937ff95a266dcfeed7b714810a55955c81e7ccc77d086af9bac0d5b18c249398a44f831f
data/.rubocop.yml CHANGED
@@ -1,19 +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:
9
+ NewCops: enable
4
10
  TargetRubyVersion: 2.4
5
11
  DisplayCopNames: true
6
12
  DisplayStyleGuide: true
13
+ Exclude:
14
+ - 'tmp/**/*'
15
+ - 'vendor/**/*'
7
16
 
8
- # We target 2.4, but still want to support 2.0.
9
- Gemspec/RequiredRubyVersion:
10
- Enabled: false
11
17
 
12
18
  Layout/LineLength:
13
19
  Enabled: false
14
20
 
21
+
15
22
  Metrics/AbcSize:
16
- Max: 53
23
+ Max: 58
17
24
 
18
25
  Metrics/BlockLength:
19
26
  Exclude:
@@ -25,19 +32,24 @@ Metrics/ClassLength:
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/.travis.yml CHANGED
@@ -3,9 +3,9 @@ sudo: false
3
3
  cache: bundler
4
4
  before_install:
5
5
  - gem update --system
6
- - gem install bundler -v '< 2'
7
6
  rvm:
8
7
  - 2.4.10
9
8
  - 2.5.9
10
9
  - 2.6.7
11
10
  - 2.7.3
11
+ - 3.0.1
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.79.0'
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 CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- docdata-order (1.0.3)
4
+ docdata-order (2.0.0)
5
5
  savon (>= 2.0, < 3.0)
6
6
 
7
7
  GEM
@@ -13,28 +13,29 @@ GEM
13
13
  gyoku (>= 0.4.0)
14
14
  nokogiri
15
15
  ast (2.4.2)
16
- builder (3.2.3)
16
+ builder (3.2.4)
17
17
  crack (0.4.3)
18
18
  safe_yaml (~> 1.0.0)
19
19
  diff-lcs (1.3)
20
20
  gyoku (1.3.1)
21
21
  builder (>= 2.1.2)
22
22
  hashdiff (0.3.4)
23
- httpi (2.4.2)
23
+ httpi (2.4.5)
24
24
  rack
25
25
  socksify
26
- jaro_winkler (1.5.4)
27
- mini_portile2 (2.3.0)
28
- nokogiri (1.8.1)
29
- mini_portile2 (~> 2.3.0)
26
+ mini_portile2 (2.4.0)
27
+ nokogiri (1.10.10)
28
+ mini_portile2 (~> 2.4.0)
30
29
  nori (2.6.0)
31
30
  parallel (1.20.1)
32
- parser (3.0.1.0)
31
+ parser (3.0.1.1)
33
32
  ast (~> 2.4.1)
34
33
  public_suffix (2.0.5)
35
- rack (2.0.3)
34
+ rack (2.2.3)
36
35
  rainbow (3.0.0)
37
36
  rake (13.0.3)
37
+ regexp_parser (2.1.1)
38
+ rexml (3.2.5)
38
39
  rspec (3.6.0)
39
40
  rspec-core (~> 3.6.0)
40
41
  rspec-expectations (~> 3.6.0)
@@ -48,26 +49,39 @@ GEM
48
49
  diff-lcs (>= 1.2.0, < 2.0)
49
50
  rspec-support (~> 3.6.0)
50
51
  rspec-support (3.6.0)
51
- rubocop (0.79.0)
52
- jaro_winkler (~> 1.5.1)
52
+ rubocop (1.12.1)
53
53
  parallel (~> 1.10)
54
- parser (>= 2.7.0.1)
54
+ parser (>= 3.0.0.0)
55
55
  rainbow (>= 2.2.2, < 4.0)
56
+ regexp_parser (>= 1.8, < 3.0)
57
+ rexml
58
+ rubocop-ast (>= 1.2.0, < 2.0)
56
59
  ruby-progressbar (~> 1.7)
57
- unicode-display_width (>= 1.4.0, < 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)
58
71
  ruby-progressbar (1.11.0)
59
72
  safe_yaml (1.0.4)
60
- savon (2.11.2)
73
+ savon (2.12.1)
61
74
  akami (~> 1.2)
62
75
  builder (>= 2.1.2)
63
76
  gyoku (~> 1.2)
64
77
  httpi (~> 2.3)
65
- nokogiri (>= 1.4.0)
78
+ nokogiri (>= 1.8.1)
66
79
  nori (~> 2.4)
67
80
  wasabi (~> 3.4)
68
81
  socksify (1.7.1)
69
- unicode-display_width (1.6.1)
70
- wasabi (3.5.0)
82
+ unicode-display_width (2.0.0)
83
+ wasabi (3.6.1)
84
+ addressable
71
85
  httpi (~> 2.0)
72
86
  nokogiri (>= 1.4.2)
73
87
  webmock (2.3.2)
@@ -79,12 +93,15 @@ PLATFORMS
79
93
  ruby
80
94
 
81
95
  DEPENDENCIES
82
- bundler (~> 1.14)
96
+ bundler (~> 2.0)
83
97
  docdata-order!
84
98
  rake (~> 13.0)
85
99
  rspec (~> 3.0)
86
- rubocop (~> 0.79.0)
100
+ rubocop (~> 1.12.0)
101
+ rubocop-performance (~> 1.10.2)
102
+ rubocop-rake (~> 0.5.1)
103
+ rubocop-rspec (~> 2.2.0)
87
104
  webmock (~> 2.3, >= 2.3.2)
88
105
 
89
106
  BUNDLED WITH
90
- 1.17.3
107
+ 2.2.17
data/README.md CHANGED
@@ -6,6 +6,25 @@
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,9 +21,9 @@ 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"
26
+ spec.add_development_dependency "bundler", "~> 2.0"
27
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"
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,11 +38,31 @@ 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
@@ -2,13 +2,27 @@
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
+
17
+ attr_accessor :payment_method, :issuers
18
+
19
+ def initialize(payment_method)
20
+ @payment_method = payment_method
21
+ end
22
+
23
+ def to_s
24
+ payment_method
25
+ end
12
26
  end
13
27
  end
14
28
  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.3"
5
+ VERSION = "2.0.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.3
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kentaa
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-04-15 00:00:00.000000000 Z
11
+ date: 2021-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,14 +16,14 @@ 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
@@ -103,7 +103,6 @@ files:
103
103
  - ".rspec"
104
104
  - ".rubocop.yml"
105
105
  - ".travis.yml"
106
- - CHANGELOG.md
107
106
  - Gemfile
108
107
  - Gemfile.lock
109
108
  - LICENSE.txt
@@ -113,10 +112,10 @@ files:
113
112
  - bin/setup
114
113
  - docdata-order.gemspec
115
114
  - lib/docdata/order.rb
115
+ - lib/docdata/order/amount.rb
116
116
  - lib/docdata/order/client.rb
117
117
  - lib/docdata/order/exception.rb
118
118
  - lib/docdata/order/gender.rb
119
- - lib/docdata/order/ideal.rb
120
119
  - lib/docdata/order/payment_method.rb
121
120
  - lib/docdata/order/request.rb
122
121
  - lib/docdata/order/response.rb
@@ -134,7 +133,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
134
133
  requirements:
135
134
  - - ">="
136
135
  - !ruby/object:Gem::Version
137
- version: 2.0.0
136
+ version: 2.4.0
138
137
  required_rubygems_version: !ruby/object:Gem::Requirement
139
138
  requirements:
140
139
  - - ">="
data/CHANGELOG.md DELETED
@@ -1,18 +0,0 @@
1
- # Docdata::Order changelog
2
-
3
- ## 1.0.3 (2021-04-15)
4
-
5
- - Added Revolut to the list of iDEAL issuers.
6
-
7
- ## 1.0.2 (2018-11-30)
8
-
9
- - Added Handelsbanken to the list of iDEAL issuers.
10
-
11
- ## 1.0.1 (2018-10-05)
12
-
13
- - Added Moneyou to the list of iDEAL issuers.
14
- - Fixed result of ExtendedStatusResponse#total_reversed.
15
-
16
- ## 1.0.0 (2018-02-09)
17
-
18
- - First public release.
@@ -1,23 +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
- "HANDNL2A" => "Handelsbanken",
11
- "INGBNL2A" => "ING",
12
- "KNABNL2H" => "Knab bank",
13
- "MOYONL21" => "Moneyou",
14
- "RABONL2U" => "Rabobank",
15
- "RBRBNL21" => "RegioBank",
16
- "REVOLT21" => "Revolut",
17
- "SNSBNL2A" => "SNS Bank",
18
- "TRIONL2U" => "Triodos Bank",
19
- "FVLBNL22" => "Van Lanschot"
20
- }.freeze
21
- end
22
- end
23
- end