1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-09-20 09:18:48 +02:00

Show prices in "$... USD" in Phortune

Summary:
Ref T2787. See discussion in D5834.

  - Replace `PhortuneUtil` with `PhortuneCurrency`, which feels a little better and more flexible / future-proof.
  - Add unit tests.
  - Display prices explicitly as "$... USD".

Test Plan: Hit product list, cart, purchase flow, verified displays.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2787

Differential Revision: https://secure.phabricator.com/D5841
This commit is contained in:
epriestley 2013-05-06 18:04:45 -07:00
parent 6eb82ebfd4
commit 5263c2d0f3
10 changed files with 214 additions and 52 deletions

View file

@ -1592,6 +1592,8 @@ phutil_register_library_map(array(
'PhortuneConstants' => 'applications/phortune/constants/PhortuneConstants.php',
'PhortuneController' => 'applications/phortune/controller/PhortuneController.php',
'PhortuneCreditCardForm' => 'applications/phortune/view/PhortuneCreditCardForm.php',
'PhortuneCurrency' => 'applications/phortune/currency/PhortuneCurrency.php',
'PhortuneCurrencyTestCase' => 'applications/phortune/currency/__tests__/PhortuneCurrencyTestCase.php',
'PhortuneDAO' => 'applications/phortune/storage/PhortuneDAO.php',
'PhortuneErrCode' => 'applications/phortune/constants/PhortuneErrCode.php',
'PhortuneLandingController' => 'applications/phortune/controller/PhortuneLandingController.php',
@ -1620,7 +1622,6 @@ phutil_register_library_map(array(
'PhortuneStripePaymentProvider' => 'applications/phortune/provider/PhortuneStripePaymentProvider.php',
'PhortuneTestExtraPaymentProvider' => 'applications/phortune/provider/__tests__/PhortuneTestExtraPaymentProvider.php',
'PhortuneTestPaymentProvider' => 'applications/phortune/provider/PhortuneTestPaymentProvider.php',
'PhortuneUtil' => 'applications/phortune/util/PhortuneUtil.php',
'PhrequentController' => 'applications/phrequent/controller/PhrequentController.php',
'PhrequentDAO' => 'applications/phrequent/storage/PhrequentDAO.php',
'PhrequentListController' => 'applications/phrequent/controller/PhrequentListController.php',
@ -3324,6 +3325,7 @@ phutil_register_library_map(array(
'PhortuneCart' => 'PhortuneDAO',
'PhortuneCharge' => 'PhortuneDAO',
'PhortuneController' => 'PhabricatorController',
'PhortuneCurrencyTestCase' => 'PhabricatorTestCase',
'PhortuneDAO' => 'PhabricatorLiskDAO',
'PhortuneErrCode' => 'PhortuneConstants',
'PhortuneLandingController' => 'PhortuneController',

View file

@ -56,9 +56,11 @@ final class PhortuneAccountBuyController
foreach ($cart->getPurchases() as $purchase) {
$rows[] = array(
$purchase->getPurchaseName(),
PhortuneUtil::formatCurrency($purchase->getBasePriceInCents()),
PhortuneCurrency::newFromUSDCents($purchase->getBasePriceInCents())
->formatForDisplay(),
$purchase->getQuantity(),
PhortuneUtil::formatCurrency($purchase->getTotalPriceInCents()),
PhortuneCurrency::newFromUSDCents($purchase->getTotalPriceInCents())
->formatForDisplay(),
);
$total += $purchase->getTotalPriceInCents();
@ -68,7 +70,8 @@ final class PhortuneAccountBuyController
phutil_tag('strong', array(), pht('Total')),
'',
'',
phutil_tag('strong', array(), PhortuneUtil::formatCurrency($total)),
phutil_tag('strong', array(),
PhortuneCurrency::newFromUSDCents($total)->formatForDisplay()),
);
$table = new AphrontTableView($rows);

View file

@ -33,7 +33,8 @@ final class PhortuneProductEditController extends PhabricatorController {
$v_name = $product->getProductName();
$v_type = $product->getProductType();
$v_price = (int)$product->getPriceInCents();
$display_price = PhortuneUtil::formatCurrency($v_price);
$display_price = PhortuneCurrency::newFromUSDCents($v_price)
->formatForDisplay();
$e_name = true;
$e_type = null;
@ -62,7 +63,8 @@ final class PhortuneProductEditController extends PhabricatorController {
$display_price = $request->getStr('price');
try {
$v_price = PhortuneUtil::parseCurrency($display_price);
$v_price = PhortuneCurrency::newFromUserInput($user, $display_price)
->getValue();
$e_price = null;
} catch (Exception $ex) {
$errors[] = pht('Price should be formatted as: $1.23');

View file

@ -44,7 +44,8 @@ final class PhortuneProductListController extends PhabricatorController {
->setObjectName($product->getID())
->setHeader($product->getProductName())
->setHref($view_uri)
->addAttribute(PhortuneUtil::formatCurrency($price))
->addAttribute(
PhortuneCurrency::newFromUSDCents($price)->formatForDisplay())
->addAttribute($product->getTypeName());
$product_list->addItem($item);

View file

@ -63,7 +63,8 @@ final class PhortuneProductViewController extends PhortuneController {
->addProperty(pht('Type'), $product->getTypeName())
->addProperty(
pht('Price'),
PhortuneUtil::formatCurrency($product->getPriceInCents()));
PhortuneCurrency::newFromUSDCents($product->getPriceInCents())
->formatForDisplay());
$xactions = id(new PhortuneProductTransactionQuery())
->setViewer($user)

View file

@ -0,0 +1,95 @@
<?php
final class PhortuneCurrency {
private $value;
private $currency;
private function __construct() {
// Intentionally private.
}
public static function newFromUserInput(PhabricatorUser $user, $string) {
$matches = null;
$ok = preg_match(
'/^([-$]*(?:\d+)?(?:[.]\d{0,2})?)(?:\s+([A-Z]+))?$/',
trim($string),
$matches);
if (!$ok) {
self::throwFormatException($string);
}
$value = $matches[1];
if (substr_count($value, '-') > 1) {
self::throwFormatException($string);
}
if (substr_count($value, '$') > 1) {
self::throwFormatException($string);
}
$value = str_replace('$', '', $value);
$value = (float)$value;
$value = (int)round(100 * $value);
$currency = idx($matches, 2, 'USD');
if ($currency) {
switch ($currency) {
case 'USD':
break;
default:
throw new Exception("Unsupported currency '{$currency}'!");
}
}
$obj = new PhortuneCurrency();
$obj->value = $value;
$obj->currency = $currency;
return $obj;
}
public static function newFromUSDCents($cents) {
if (!is_int($cents)) {
throw new Exception("USDCents value is not an integer!");
}
$obj = new PhortuneCurrency();
$obj->value = $cents;
$obj->currency = 'USD';
return $obj;
}
public function formatForDisplay() {
$bare = $this->formatBareValue();
return '$'.$bare.' USD';
}
public function formatBareValue() {
switch ($this->currency) {
case 'USD':
return sprintf('%.02f', $this->value / 100);
default:
throw new Exception("Unsupported currency!");
}
}
public function getValue() {
return $this->value;
}
public function getCurrency() {
return $this->currency;
}
private static function throwFormatException($string) {
throw new Exception("Invalid currency format ('{$string}').");
}
}

View file

@ -0,0 +1,93 @@
<?php
final class PhortuneCurrencyTestCase extends PhabricatorTestCase {
public function testCurrencyFormatForDisplay() {
$map = array(
0 => '$0.00 USD',
1 => '$0.01 USD',
100 => '$1.00 USD',
-123 => '$-1.23 USD',
5000000 => '$50000.00 USD',
);
foreach ($map as $input => $expect) {
$this->assertEqual(
$expect,
PhortuneCurrency::newFromUSDCents($input)->formatForDisplay(),
"formatForDisplay({$input})");
}
}
public function testCurrencyFormatBareValue() {
// NOTE: The PayPal API depends on the behavior of the bare value format!
$map = array(
0 => '0.00',
1 => '0.01',
100 => '1.00',
-123 => '-1.23',
5000000 => '50000.00',
);
foreach ($map as $input => $expect) {
$this->assertEqual(
$expect,
PhortuneCurrency::newFromUSDCents($input)->formatBareValue(),
"formatBareValue({$input})");
}
}
public function testCurrencyFromUserInput() {
$map = array(
'1.00' => 100,
'1.00 USD' => 100,
'$1.00' => 100,
'$1.00 USD' => 100,
'-$1.00 USD' => -100,
'$-1.00 USD' => -100,
'1' => 100,
'.99' => 99,
'$.99' => 99,
'-$.99' => -99,
'$-.99' => -99,
'$.99 USD' => 99,
);
$user = new PhabricatorUser();
foreach ($map as $input => $expect) {
$this->assertEqual(
$expect,
PhortuneCurrency::newFromUserInput($user, $input)->getValue(),
"newFromUserInput({$input})->getValue()");
}
}
public function testInvalidCurrencyFromUserInput() {
$map = array(
'--1',
'$$1',
'1 JPY',
'buck fiddy',
'1.2.3',
'1 dollar',
);
$user = new PhabricatorUser();
foreach ($map as $input) {
$caught = null;
try {
PhortuneCurrency::newFromUserInput($user, $input);
} catch (Exception $ex) {
$caught = $ex;
}
$this->assertEqual(true, ($caught instanceof Exception), "{$input}");
}
}
}

View file

@ -122,15 +122,15 @@ final class PhortunePaypalPaymentProvider extends PhortunePaymentProvider {
));
$total_in_cents = $cart->getTotalInCents();
$price = PhortuneUtil::formatBareCurrency($total_in_cents);
$price = PhortuneCurrency::newFromUSDCents($total_in_cents);
$result = $this
->newPaypalAPICall()
->setRawPayPalQuery(
'SetExpressCheckout',
array(
'PAYMENTREQUEST_0_AMT' => $price,
'PAYMENTREQUEST_0_CURRENCYCODE' => 'USD',
'PAYMENTREQUEST_0_AMT' => $price->formatBareValue(),
'PAYMENTREQUEST_0_CURRENCYCODE' => $price->getCurrency(),
'RETURNURL' => $return_uri,
'CANCELURL' => $cancel_uri,
'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',

View file

@ -48,13 +48,16 @@ final class PhortuneProductTransaction
return pht(
'%s set product price to %s.',
$this->renderHandleLink($author_phid),
PhortuneUtil::formatCurrency($new));
PhortuneCurrency::newFromUSDCents($new)
->formatForDisplay());
} else {
return pht(
'%s changed product price from %s to %s.',
$this->renderHandleLink($author_phid),
PhortuneUtil::formatCurrency($old),
PhortuneUtil::formatCurrency($new));
PhortuneCurrency::newFromUSDCents($old)
->formatForDisplay(),
PhortuneCurrency::newFromUSDCents($new)
->formatForDisplay());
}
break;
case self::TYPE_TYPE:

View file

@ -1,38 +0,0 @@
<?php
final class PhortuneUtil {
public static function parseCurrency($string) {
$string = trim($string);
if (!preg_match('/^[-]?[$]?\d+([.]\d{0,2})?$/', $string)) {
throw new Exception("Invalid currency format ('{$string}').");
}
$value = str_replace('$', '', $string);
$value = (float)$value;
$value = (int)round(100 * $value);
return $value;
}
public static function formatCurrency($price_in_cents) {
if (!preg_match('/^[-]?\d+$/', $price_in_cents)) {
throw new Exception("Invalid price in cents ('{$price_in_cents}').");
}
$negative = ($price_in_cents < 0);
$price_in_cents = abs($price_in_cents);
$display_value = sprintf('$%.02f', $price_in_cents / 100);
if ($negative) {
$display_value = '-'.$display_value;
}
return $display_value;
}
public static function formatBareCurrency($price_in_cents) {
return str_replace('$', '', self::formatCurrency($price_in_cents));
}
}