mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-02 09:58:24 +01: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:
parent
6eb82ebfd4
commit
5263c2d0f3
10 changed files with 214 additions and 52 deletions
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
95
src/applications/phortune/currency/PhortuneCurrency.php
Normal file
95
src/applications/phortune/currency/PhortuneCurrency.php
Normal 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}').");
|
||||
}
|
||||
|
||||
}
|
|
@ -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}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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',
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue