braintree / braintree_java

Braintree Java library
https://developer.paypal.com/braintree/docs/start/overview
MIT License
158 stars 98 forks source link

Braintree API returning invalid currency scale #33

Closed jart closed 8 years ago

jart commented 8 years ago

I'm testing my app with the Sandbox API.

If I issue a transaction for 3 JPY, the Braintree API will send me back a success response containing "3.00 JPY". This is an error, because Japanese Yen can't have decimal points.

For example:

Result<Transaction> result =
        braintreeGateway.transaction().sale(
            new TransactionRequest()
                .amount(new BigDecimal("3"))
                .paymentMethodNonce("omg-im-a-nonce")
                .merchantAccountId("jpy-merchant-account")
                .options()
                    .submitForSettlement(true)
                    .done());
Transaction transaction = result.getTarget();
Money.of(CurrencyUnit.of(transaction.getCurrencyIsoCode()), transaction.getAmount());

Causes:

java.lang.ArithmeticException: Scale of amount 3.00 is greater than the scale of the currency JPY
    at org.joda.money.Money.of(Money.java:74)
crookedneighbor commented 8 years ago

Thanks for bringing this up. We're discussing internally what should be done about this and we’ll get back to you once we have investigated further.

raymondberg commented 8 years ago

@jart, thanks for sending this note! Short answer: A supported way of handling this for JPY is to simply call .intValue() on the BigDecimal objects using JPY. As you point out, - in the case of JPY - there would be no loss of precision by removing the decimals. I hope this helps!

Background: Our system is built this way as a common interface for the many currencies we support, most of which require multiple decimal places. This can cause some confusion for non-subdivided currencies like JPY (or others like BYR, CLP, BIF, ISK, VND), but the uniformity for merchants to handle all currencies with a minimal fieldset is worth it!

The client libraries are intended to be lightweight to avoid frequent changes in multiple locations that might slow down added features in our API. Implementing conversion logic for languages we support specifically for would require custom logic that would make the libraries more brittle and slow down new features coming from the gateway. For these reasons, the better approach would seem to let merchants to extract the currency values that make sense for their business.

jart commented 8 years ago

The workaround we're using is:

Money amount =
    Money.of(CurrencyUnit.of(transaction.getCurrencyIsoCode()),
        transaction.getAmount().stripTrailingZeros());