docdata-order 1.0.3 → 2.0.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
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