Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/mollie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ module Mollie
require 'mollie/order/shipment'
require 'mollie/payment/capture'
require 'mollie/payment/chargeback'
require 'mollie/payment/line'
require 'mollie/payment/refund'
require 'mollie/settlement/capture'
require 'mollie/settlement/chargeback'
Expand Down
16 changes: 16 additions & 0 deletions lib/mollie/payment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ class Payment < Base
:cancel_url,
:webhook_url

attr_reader :lines,
:billing_address,
:shipping_address

alias links _links

def open?
Expand Down Expand Up @@ -185,6 +189,18 @@ def amount_refunded=(amount_refunded)
@amount_refunded = Mollie::Amount.new(amount_refunded)
end

def lines=(lines)
@lines = lines.map { |line| Payment::Line.new(line) }
end

def billing_address=(address)
@billing_address = OpenStruct.new(address) if address.is_a?(Hash)
end

def shipping_address=(address)
@shipping_address = OpenStruct.new(address) if address.is_a?(Hash)
end

def checkout_url
Util.extract_url(links, 'checkout')
end
Expand Down
50 changes: 50 additions & 0 deletions lib/mollie/payment/line.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
module Mollie
class Payment
class Line < Base
attr_accessor :type,
:description,
:quantity,
:quantity_unit,
:vat_rate,
:sku,
:categories,
:image_url,
:product_url

attr_reader :unit_price,
:discount_amount,
:recurring,
:total_amount,
:vat_amount

def discounted?
!@discount_amount.nil?
end

def unit_price=(amount)
@unit_price = Mollie::Amount.new(amount)
end

def discount_amount=(amount)
@discount_amount = Mollie::Amount.new(amount)
end

def recurring=(object)
return if object.nil?

@recurring = OpenStruct.new(object).tap do |r|
r.amount = Mollie::Amount.new(r.amount) if r.amount
r.start_date = Date.parse(r.start_date) if r.start_date
end
end

def total_amount=(amount)
@total_amount = Mollie::Amount.new(amount)
end

def vat_amount=(amount)
@vat_amount = Mollie::Amount.new(amount)
end
end
end
end
28 changes: 25 additions & 3 deletions lib/mollie/util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,31 @@ def nested_underscore_keys(obj)
end
end

def camelize_keys(hash)
hash.each_with_object({}) do |(key, value), camelized|
camelized[camelize(key)] = value
CAMELIZE_NESTED = [
:lines,
"lines",
:recurring,
"recurring",
:billing_address,
"billing_address",
:shipping_address,
"shipping_address"
].freeze

def camelize_keys(object)
case object
when Hash
object.each_with_object({}) do |(key, value), camelized|
camelized[camelize(key)] = if CAMELIZE_NESTED.include?(key)
camelize_keys(value)
else
value
end
end
when Array
object.map { |value| camelize_keys(value) }
else
object
end
end

Expand Down
129 changes: 129 additions & 0 deletions test/mollie/payment_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,135 @@ def test_refunded?
assert_false Payment.new([]).refunded?
end

def test_payment_lines_attributes
attributes = {
resource: "payment",
id: "tr_7UhSN1zuXS",
lines: [{
type: "physical",
description: "LEGO 4440 Forest Police Station",
quantity: 1,
quantity_unit: "pcs",
unit_price: { value: "89.00", currency: "EUR" },
discount_amount: { value: "10.00", currency: "EUR" },
recurring: {
description: "A description of the recurring item.",
interval: "3 months",
amount: { value: "29.00", currency: "EUR" },
times: 4,
start_date: "2025-05-04"
},
total_amount: { value: "95.59", currency: "EUR" },
vat_rate: "21.00",
vat_amount: { value: "16.59", currency: "EUR" },
sku: "SKU-12345",
categories: ["eco", "meal", "gift", "sport_culture"],
image_url: "https://example.org/lego-4440-forest-police-station.jpg",
product_url: "https://example.org/lego-4440-forest-police-station"
}]
}

payment = Payment.new(attributes)
line = payment.lines.first

assert_equal Payment::Line, line.class
assert_equal "physical", line.type
assert_equal "LEGO 4440 Forest Police Station", line.description
assert_equal 1, line.quantity
assert_equal "pcs", line.quantity_unit
assert_equal 89.00, line.unit_price.value
assert_equal "EUR", line.unit_price.currency
assert_equal 10.00, line.discount_amount.value
assert_equal "EUR", line.discount_amount.currency
assert_equal 95.59, line.total_amount.value
assert_equal "EUR", line.total_amount.currency
assert_equal "21.00", line.vat_rate
assert_equal 16.59, line.vat_amount.value
assert_equal "EUR", line.vat_amount.currency
assert_equal "SKU-12345", line.sku
assert_equal ["eco", "meal", "gift", "sport_culture"], line.categories
assert_equal "https://example.org/lego-4440-forest-police-station.jpg", line.image_url
assert_equal "https://example.org/lego-4440-forest-police-station", line.product_url

# Recurring object
assert_equal "A description of the recurring item.", line.recurring.description
assert_equal "3 months", line.recurring.interval
assert_equal 29.00, line.recurring.amount.value
assert_equal "EUR", line.recurring.amount.currency
assert_equal 4, line.recurring.times
assert_equal Date.parse("2025-05-04"), line.recurring.start_date
end

def test_billing_address
attributes = {
resource: "payment",
id: "tr_7UhSN1zuXS",
billing_address: {
title: "Dhr",
given_name: "Piet",
family_name: "Mondriaan",
organization_name: "Mollie B.V.",
street_and_number: "Keizersgracht 313",
street_additional: "apt",
postal_code: "1234AB",
email: "piet@mondriaan.com",
phone: "+31208202070",
city: "Amsterdam",
region: "Noord-Holland",
country: "NL"
}
}

payment = Payment.new(attributes)
assert_equal "Dhr", payment.billing_address.title
assert_equal "Piet", payment.billing_address.given_name
assert_equal "Mondriaan", payment.billing_address.family_name
assert_equal "Mollie B.V.", payment.billing_address.organization_name
assert_equal "Keizersgracht 313", payment.billing_address.street_and_number
assert_equal "apt", payment.billing_address.street_additional
assert_equal "1234AB", payment.billing_address.postal_code
assert_equal "piet@mondriaan.com", payment.billing_address.email
assert_equal "+31208202070", payment.billing_address.phone
assert_equal "Amsterdam", payment.billing_address.city
assert_equal "Noord-Holland", payment.billing_address.region
assert_equal "NL", payment.billing_address.country
end

def test_shipping_address
attributes = {
resource: "payment",
id: "tr_7UhSN1zuXS",
shipping_address: {
title: "Dhr",
given_name: "Piet",
family_name: "Mondriaan",
organization_name: "Mollie B.V.",
street_and_number: "Keizersgracht 313",
street_additional: "apt",
postal_code: "1234AB",
email: "piet@mondriaan.com",
phone: "+31208202070",
city: "Amsterdam",
region: "Noord-Holland",
country: "NL"
}
}

payment = Payment.new(attributes)
assert_equal "Dhr", payment.shipping_address.title
assert_equal "Piet", payment.shipping_address.given_name
assert_equal "Mondriaan", payment.shipping_address.family_name
assert_equal "Mollie B.V.", payment.shipping_address.organization_name
assert_equal "Keizersgracht 313", payment.shipping_address.street_and_number
assert_equal "apt", payment.shipping_address.street_additional
assert_equal "1234AB", payment.shipping_address.postal_code
assert_equal "piet@mondriaan.com", payment.shipping_address.email
assert_equal "+31208202070", payment.shipping_address.phone
assert_equal "Amsterdam", payment.shipping_address.city
assert_equal "Noord-Holland", payment.shipping_address.region
assert_equal "NL", payment.shipping_address.country
end

def test_create_payment
stub_request(:post, 'https://api.mollie.com/v2/payments')
.with(body: %({"amount":{"value":1.95,"currency":"EUR"}}))
Expand Down