Skip to content

Commit

Permalink
Merge pull request #90 from afterpay/currency-formatting
Browse files Browse the repository at this point in the history
Update currency formatting to respect locale and follow Afterpay business rules
  • Loading branch information
adamjcampbell authored Oct 22, 2020
2 parents bbd5385 + 27a5f66 commit 6e18e8a
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 27 deletions.
15 changes: 5 additions & 10 deletions afterpay/src/main/kotlin/com/afterpay/android/Afterpay.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,6 @@ object Afterpay {
internal val locale: Locale
get() = configuration?.locale ?: Locales.US

private val validLocaleCountries = setOf(
Locales.AUSTRALIA.country,
Locales.CANADA.country,
Locales.UK.country,
Locales.NEW_ZEALAND.country,
Locales.US.country
)

/**
* Creates an [Intent] that can be used to initiate an Afterpay transaction. Provide the
* new [Intent] in [startActivityForResult][android.app.Activity.startActivityForResult]
Expand Down Expand Up @@ -104,10 +96,13 @@ object Afterpay {
throw IllegalArgumentException("Minimum order amount is invalid")
}
}
if (!validLocaleCountries.contains(configuration.locale.country)) {

val validCountries = Locales.validSet.map { it.country }

if (!validCountries.contains(configuration.locale.country)) {
throw IllegalArgumentException(
"Locale contains an unsupported country: ${configuration.locale.country}. " +
"Supported countries include: ${validLocaleCountries.joinToString(",")}"
"Supported countries include: ${validCountries.joinToString(",")}"
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.afterpay.android.internal

import com.afterpay.android.Afterpay
import java.math.BigDecimal
import java.math.RoundingMode
import java.text.DecimalFormat
import java.text.NumberFormat
import java.util.Currency

internal sealed class AfterpayInstalment {
data class Available(
Expand All @@ -19,19 +19,35 @@ internal sealed class AfterpayInstalment {
object NoConfiguration : AfterpayInstalment()

companion object {
fun of(totalCost: BigDecimal): AfterpayInstalment {
val configuration = Afterpay.configuration ?: return NoConfiguration

val currencyFormatter = (NumberFormat.getCurrencyInstance() as DecimalFormat).apply {
currency = configuration.currency
decimalFormatSymbols = decimalFormatSymbols.apply {
currencySymbol = when (configuration.currency.currencyCode) {
"AUD" -> "A$"
"NZD" -> "NZ$"
"CAD" -> "CA$"
else -> "$"
fun of(totalCost: BigDecimal, configuration: Configuration?): AfterpayInstalment {
if (configuration == null) {
return NoConfiguration
}

val currencyLocale = Locales.validSet.first { Currency.getInstance(it) == configuration.currency }
val currencySymbol = configuration.currency.getSymbol(currencyLocale)
val usCurrencySymbol = Currency.getInstance(Locales.US).getSymbol(Locales.US)
val localCurrency = Currency.getInstance(configuration.locale)

val currencyFormatter = (NumberFormat.getCurrencyInstance(currencyLocale) as DecimalFormat).apply {
this.currency = configuration.currency
}

if (configuration.locale == Locales.US) {
currencyFormatter.apply {
decimalFormatSymbols = decimalFormatSymbols.apply {
this.currencySymbol = when (configuration.currency) {
Currency.getInstance(Locales.AUSTRALIA) -> "A$"
Currency.getInstance(Locales.NEW_ZEALAND) -> "NZ$"
Currency.getInstance(Locales.CANADA) -> "CA$"
else -> currencySymbol
}
}
}
} else if (currencySymbol == usCurrencySymbol && configuration.currency != localCurrency) {
currencyFormatter.apply {
this.applyPattern("¤#,##0.00 ¤¤")
}
}

val minimumAmount = configuration.minimumAmount ?: BigDecimal.ZERO
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package com.afterpay.android.internal

import java.util.Locale

object Locales {
internal object Locales {

val AUSTRALIA = Locale("en", "AU")
val CANADA = Locale.CANADA
val CANADA: Locale = Locale.CANADA
val NEW_ZEALAND = Locale("en", "NZ")
val UK = Locale.UK
val US = Locale.US
val UK: Locale = Locale.UK
val US: Locale = Locale.US

val validSet = setOf(AUSTRALIA, CANADA, UK, NEW_ZEALAND, US)
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class AfterpayPriceBreakdown(context: Context, attrs: AttributeSet?) : FrameLayo
setBounds(0, 0, drawableWidth.toInt(), drawableHeight.toInt())
}

val instalment = AfterpayInstalment.of(totalAmount)
val instalment = AfterpayInstalment.of(totalAmount, Afterpay.configuration)
val content = generateContent(instalment)

textView.apply {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package com.afterpay.android

import com.afterpay.android.internal.AfterpayInstalment
import com.afterpay.android.internal.Configuration
import com.afterpay.android.internal.Locales
import org.junit.Assert.assertEquals
import org.junit.Test
import java.math.BigDecimal
import java.util.Currency
import java.util.Locale

class AfterpayInstalmentTest {
private val australianDollar: Currency = Currency.getInstance("AUD")
private val canadianDollar: Currency = Currency.getInstance("CAD")
private val poundSterling: Currency = Currency.getInstance("GBP")
private val newZealandDollar: Currency = Currency.getInstance("NZD")
private val unitedStatesDollar: Currency = Currency.getInstance("USD")

private val oneHundredAndTwenty = 120.toBigDecimal()

@Test
fun `available instalment in Australia locale`() {
val locale = Locales.AUSTRALIA

val audInstalment = availableInstalment(oneHundredAndTwenty, australianDollar, locale)
val cadInstalment = availableInstalment(oneHundredAndTwenty, canadianDollar, locale)
val gbpInstalment = availableInstalment(oneHundredAndTwenty, poundSterling, locale)
val nzdInstalment = availableInstalment(oneHundredAndTwenty, newZealandDollar, locale)
val usdInstalment = availableInstalment(oneHundredAndTwenty, unitedStatesDollar, locale)

assertEquals("$30.00", audInstalment.instalmentAmount)
assertEquals("$30.00 CAD", cadInstalment.instalmentAmount)
assertEquals("£30.00", gbpInstalment.instalmentAmount)
assertEquals("$30.00 NZD", nzdInstalment.instalmentAmount)
assertEquals("$30.00 USD", usdInstalment.instalmentAmount)
}

@Test
fun `available instalment in Canada locale`() {
val locale = Locales.CANADA

val audInstalment = availableInstalment(oneHundredAndTwenty, australianDollar, locale)
val cadInstalment = availableInstalment(oneHundredAndTwenty, canadianDollar, locale)
val gbpInstalment = availableInstalment(oneHundredAndTwenty, poundSterling, locale)
val nzdInstalment = availableInstalment(oneHundredAndTwenty, newZealandDollar, locale)
val usdInstalment = availableInstalment(oneHundredAndTwenty, unitedStatesDollar, locale)

assertEquals("$30.00 AUD", audInstalment.instalmentAmount)
assertEquals("$30.00", cadInstalment.instalmentAmount)
assertEquals("£30.00", gbpInstalment.instalmentAmount)
assertEquals("$30.00 NZD", nzdInstalment.instalmentAmount)
assertEquals("$30.00 USD", usdInstalment.instalmentAmount)
}

@Test
fun `available instalment in UK locale`() {
val locale = Locales.UK

val audInstalment = availableInstalment(oneHundredAndTwenty, australianDollar, locale)
val cadInstalment = availableInstalment(oneHundredAndTwenty, canadianDollar, locale)
val gbpInstalment = availableInstalment(oneHundredAndTwenty, poundSterling, locale)
val nzdInstalment = availableInstalment(oneHundredAndTwenty, newZealandDollar, locale)
val usdInstalment = availableInstalment(oneHundredAndTwenty, unitedStatesDollar, locale)

assertEquals("$30.00 AUD", audInstalment.instalmentAmount)
assertEquals("$30.00 CAD", cadInstalment.instalmentAmount)
assertEquals("£30.00", gbpInstalment.instalmentAmount)
assertEquals("$30.00 NZD", nzdInstalment.instalmentAmount)
assertEquals("$30.00 USD", usdInstalment.instalmentAmount)
}

@Test
fun `available instalment in New Zealand locale`() {
val locale = Locales.NEW_ZEALAND

val audInstalment = availableInstalment(oneHundredAndTwenty, australianDollar, locale)
val cadInstalment = availableInstalment(oneHundredAndTwenty, canadianDollar, locale)
val gbpInstalment = availableInstalment(oneHundredAndTwenty, poundSterling, locale)
val nzdInstalment = availableInstalment(oneHundredAndTwenty, newZealandDollar, locale)
val usdInstalment = availableInstalment(oneHundredAndTwenty, unitedStatesDollar, locale)

assertEquals("$30.00 AUD", audInstalment.instalmentAmount)
assertEquals("$30.00 CAD", cadInstalment.instalmentAmount)
assertEquals("£30.00", gbpInstalment.instalmentAmount)
assertEquals("$30.00", nzdInstalment.instalmentAmount)
assertEquals("$30.00 USD", usdInstalment.instalmentAmount)
}

@Test
fun `available instalment in US locale`() {
val locale = Locales.US

val audInstalment = availableInstalment(oneHundredAndTwenty, australianDollar, locale)
val cadInstalment = availableInstalment(oneHundredAndTwenty, canadianDollar, locale)
val gbpInstalment = availableInstalment(oneHundredAndTwenty, poundSterling, locale)
val nzdInstalment = availableInstalment(oneHundredAndTwenty, newZealandDollar, locale)
val usdInstalment = availableInstalment(oneHundredAndTwenty, unitedStatesDollar, locale)

assertEquals("A$30.00", audInstalment.instalmentAmount)
assertEquals("CA$30.00", cadInstalment.instalmentAmount)
assertEquals("£30.00", gbpInstalment.instalmentAmount)
assertEquals("NZ$30.00", nzdInstalment.instalmentAmount)
assertEquals("$30.00", usdInstalment.instalmentAmount)
}

private fun availableInstalment(
amount: BigDecimal,
currency: Currency,
locale: Locale
): AfterpayInstalment.Available {
val configuration = Configuration(50.toBigDecimal(), 1000.toBigDecimal(), currency, locale)
return AfterpayInstalment.of(amount, configuration) as AfterpayInstalment.Available
}
}

0 comments on commit 6e18e8a

Please sign in to comment.