Skip to content

Commit

Permalink
Add currency formatting for amounts
Browse files Browse the repository at this point in the history
excid3 committed Sep 11, 2021
1 parent f990a38 commit dfbc2a4
Showing 11 changed files with 2,832 additions and 9 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
### Unreleased

* Add `Pay::Currency` for formatting amounts with currency - @excid3
* Add `amount_with_currency` and `amount_refunded_with_currency` to `Pay::Charge` - @excid3
* Safer pay processor lookup when processor is blank - @excid3
* Store `stripe_account` when syncing Stripe payment methods - @excid3

### 3.0.12
8 changes: 8 additions & 0 deletions app/models/pay/charge.rb
Original file line number Diff line number Diff line change
@@ -71,6 +71,14 @@ def partial_refund?
refunded? && !full_refund?
end

def amount_with_currency
Pay::Currency.format(amount.to_i, currency: currency)
end

def amount_refunded_with_currency
Pay::Currency.format(amount_refunded.to_i, currency: currency)
end

def charged_to
case payment_method_type
when "card"
4 changes: 2 additions & 2 deletions app/views/pay/payments/show.html.erb
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@
<% else %>
<div data-payment-intent-target="form" id="payment-elements">
<!-- Instructions -->
<h1 class="text-xl mt-2 mb-4 text-gray-700"><%=t "pay.requires_action.header", amount: number_to_currency(@payment.amount / 100.0) %></h1>
<h1 class="text-xl mt-2 mb-4 text-gray-700"><%=t "pay.requires_action.header", amount: @payment.amount_with_currency %></h1>
<p class="mb-6"><%=t "pay.requires_action.description" %></p>

<div data-payment-intent-target="cardFields" class="hidden">
@@ -49,7 +49,7 @@

<!-- Pay Button -->
<button data-payment-intent-target="button" data-action="click->payment-intent#confirmPayment" class="inline-block w-full px-4 py-3 mb-4 text-white rounded-lg bg-blue-400 hover:bg-blue-500">
<%=t "pay.requires_action.button", amount: number_to_currency(@payment.amount / 100.0) %>
<%=t "pay.requires_action.button", amount: @payment.amount_with_currency %>
</button>
</div>
<% end %>
2 changes: 1 addition & 1 deletion app/views/pay/user_mailer/receipt.html.erb
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ Questions? Please reply to this email.<br/>
------------------------------------<br/>
RECEIPT - SUBSCRIPTION<br/>
<br/>
Amount: USD <%= ActionController::Base.helpers.number_to_currency(params[:charge].amount / 100.0) %><br/>
Amount: USD <%= params[:charge].amount_with_currency %><br/>
<br/>
Charged to: <%= params[:charge].charged_to %><br/>
Transaction ID: <%= params[:charge].id %><br/>
2 changes: 1 addition & 1 deletion app/views/pay/user_mailer/refund.html.erb
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ Questions? Please reply to this email.<br/>
------------------------------------<br/>
RECEIPT - REFUND<br/>
<br/>
Amount: USD <%= ActionController::Base.helpers.number_to_currency(params[:charge].amount / 100.0) %><br/>
Amount: USD <%= params[:charge].amount_refunded_with_currency %><br/>
<br/>
Refunded to: <%= params[:charge].charged_to %><br/>
Transaction ID: <%= params[:charge].id %><br/>
2,702 changes: 2,702 additions & 0 deletions config/currencies/iso.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions lib/pay.rb
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ module Pay
autoload :NanoId, "pay/nano_id"
autoload :Payment, "pay/payment"
autoload :Receipts, "pay/receipts"
autoload :Currency, "pay/currency"

# Payment processors
autoload :Braintree, "pay/braintree"
74 changes: 74 additions & 0 deletions lib/pay/currency.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
module Pay
class Currency
include ActionView::Helpers::NumberHelper

attr_reader :attributes

def self.all
@currencies ||= begin
path = Engine.root.join("config", "currencies", "iso.json")
JSON.parse File.read(path)
end
end

# Takes an amount (in cents) and currency and returns the formatted version for the currency
def self.format(amount, currency:)
currency ||= :usd
new(currency).format_amount(amount)
end

def initialize(iso_code)
@attributes = self.class.all[iso_code.to_s.downcase]
end

def format_amount(amount, **options)
number_to_currency(
amount / subunit_to_unit.to_f,
{
precision: precision,
unit: unit,
separator: separator,
delimiter: delimiter,
format: format
}.compact.merge(options)
)
end

# Returns the precision to display
#
# If 1, returns 0
# If 100, returns 2
# If 1000, returns 3
def precision
subunit_to_unit.digits.count - 1
end

def unit
attributes["unit"]
end

def separator
attributes["separator"]
end

def delimiter
attributes["delimiter"]
end

def format
attributes["format"]
end

def subunit?
subunit.blank?
end

def subunit
attributes["subunit"]
end

def subunit_to_unit
attributes["subunit_to_unit"]
end
end
end
8 changes: 3 additions & 5 deletions lib/pay/receipts.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
module Pay
module Receipts
include ActionView::Helpers::NumberHelper

def product
Pay.application_name
end
@@ -20,11 +18,11 @@ def receipt_pdf(**options)
[I18n.t("pay.receipt.date"), I18n.l(created_at, format: :long)],
[I18n.t("pay.receipt.account_billed"), "#{customer.customer_name} (#{customer.email})"],
[I18n.t("pay.receipt.product"), product],
[I18n.t("pay.receipt.amount"), number_to_currency(amount / 100.0)],
[I18n.t("pay.receipt.amount"), Pay::Currency.format(amount, currency: currency)],
[I18n.t("pay.receipt.charged_to"), charged_to]
]
line_items << [I18n.t("pay.receipt.additional_info"), customer.owner.extra_billing_info] if customer.owner.extra_billing_info?
line_items << [I18n.t("pay.receipt.refunded"), number_to_currency(amount_refunded / 100.0)] if refunded?
line_items << [I18n.t("pay.receipt.refunded"), Pay::Currency.format(amount_refunded, currency: currency)] if refunded?

defaults = {
id: id,
@@ -54,7 +52,7 @@ def invoice_pdf(**options)
bill_to += [customer.owner.extra_billing_info] if customer.owner.extra_billing_info?
bill_to += [nil, customer.owner.email]

total = ActionController::Base.helpers.number_to_currency(amount / 100.0)
total = Pay::Currency.format(amount, currency: currency)

line_items = [
["<b>#{I18n.t("pay.invoice.product")}</b>", nil, "<b>#{I18n.t("pay.invoice.amount")}</b>"],
20 changes: 20 additions & 0 deletions test/models/pay/charge_test.rb
Original file line number Diff line number Diff line change
@@ -77,4 +77,24 @@ class Pay::Charge::Test < ActiveSupport::TestCase
refute Pay::Charge.new(amount: 1_00, amount_refunded: 0).partial_refund?
refute Pay::Charge.new(amount: 1_00, amount_refunded: nil).partial_refund?
end

test "amount_with_currency" do
assert_equal "$0.00", Pay::Charge.new(amount: nil, currency: nil).amount_with_currency
assert_equal "$123.45", Pay::Charge.new(amount: 123_45, currency: :usd).amount_with_currency
assert_equal "€123,45", Pay::Charge.new(amount: 123_45, currency: :eur).amount_with_currency
assert_equal "£123.45", Pay::Charge.new(amount: 123_45, currency: :gbp).amount_with_currency
assert_equal "¥12,345", Pay::Charge.new(amount: 123_45, currency: :jpy).amount_with_currency
assert_equal "12.345 ع.د", Pay::Charge.new(amount: 123_45, currency: :iqd).amount_with_currency
assert_equal "12 345 Ft", Pay::Charge.new(amount: 123_45, currency: :huf).amount_with_currency
end

test "amount_refunded_with_currency" do
assert_equal "$0.00", Pay::Charge.new(amount_refunded: nil, currency: nil).amount_with_currency
assert_equal "$123.45", Pay::Charge.new(amount_refunded: 123_45, currency: :usd).amount_refunded_with_currency
assert_equal "€123,45", Pay::Charge.new(amount_refunded: 123_45, currency: :eur).amount_refunded_with_currency
assert_equal "£123.45", Pay::Charge.new(amount_refunded: 123_45, currency: :gbp).amount_refunded_with_currency
assert_equal "¥12,345", Pay::Charge.new(amount_refunded: 123_45, currency: :jpy).amount_refunded_with_currency
assert_equal "12.345 ع.د", Pay::Charge.new(amount_refunded: 123_45, currency: :iqd).amount_refunded_with_currency
assert_equal "12 345 Ft", Pay::Charge.new(amount_refunded: 123_45, currency: :huf).amount_refunded_with_currency
end
end
17 changes: 17 additions & 0 deletions test/pay/currency_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require "test_helper"

class Pay::Currency::Test < ActiveSupport::TestCase
test "formats amounts in different currencies" do
assert_equal "$15.39", Pay::Currency.format(15_39, currency: :usd)
assert_equal "1 539 Ft", Pay::Currency.format(15_39, currency: :huf)
assert_equal "€15,39", Pay::Currency.format(15_39, currency: :eur)
assert_equal "¥1,539", Pay::Currency.format(15_39, currency: :jpy)
assert_equal "¥15.39", Pay::Currency.format(15_39, currency: :cny)
assert_equal "£15.39", Pay::Currency.format(15_39, currency: :gbp)
assert_equal "1.539 ع.د", Pay::Currency.format(15_39, currency: :iqd)
end

test "defaults to :usd if currency nil" do
assert_equal "$15.39", Pay::Currency.format(15_39, currency: nil)
end
end

0 comments on commit dfbc2a4

Please sign in to comment.