diff --git a/src/applications/phortune/editor/PhortuneCartEditor.php b/src/applications/phortune/editor/PhortuneCartEditor.php --- a/src/applications/phortune/editor/PhortuneCartEditor.php +++ b/src/applications/phortune/editor/PhortuneCartEditor.php @@ -3,6 +3,21 @@ final class PhortuneCartEditor extends PhabricatorApplicationTransactionEditor { + private $invoiceIssues; + + public function setInvoiceIssues(array $invoice_issues) { + $this->invoiceIssues = $invoice_issues; + return $this; + } + + public function getInvoiceIssues() { + return $this->invoiceIssues; + } + + public function isInvoice() { + return (bool)$this->invoiceIssues; + } + public function getEditorApplicationClass() { return 'PhabricatorPhortuneApplication'; } @@ -20,6 +35,7 @@ $types[] = PhortuneCartTransaction::TYPE_REVIEW; $types[] = PhortuneCartTransaction::TYPE_CANCEL; $types[] = PhortuneCartTransaction::TYPE_REFUND; + $types[] = PhortuneCartTransaction::TYPE_INVOICED; return $types; } @@ -35,6 +51,7 @@ case PhortuneCartTransaction::TYPE_REVIEW: case PhortuneCartTransaction::TYPE_CANCEL: case PhortuneCartTransaction::TYPE_REFUND: + case PhortuneCartTransaction::TYPE_INVOICED: return null; } @@ -52,6 +69,7 @@ case PhortuneCartTransaction::TYPE_REVIEW: case PhortuneCartTransaction::TYPE_CANCEL: case PhortuneCartTransaction::TYPE_REFUND: + case PhortuneCartTransaction::TYPE_INVOICED: return $xaction->getNewValue(); } @@ -69,6 +87,7 @@ case PhortuneCartTransaction::TYPE_REVIEW: case PhortuneCartTransaction::TYPE_CANCEL: case PhortuneCartTransaction::TYPE_REFUND: + case PhortuneCartTransaction::TYPE_INVOICED: return; } @@ -86,6 +105,7 @@ case PhortuneCartTransaction::TYPE_REVIEW: case PhortuneCartTransaction::TYPE_CANCEL: case PhortuneCartTransaction::TYPE_REFUND: + case PhortuneCartTransaction::TYPE_INVOICED: return; } @@ -113,6 +133,25 @@ $body = parent::buildMailBody($object, $xactions); + if ($this->isInvoice()) { + $issues = $this->getInvoiceIssues(); + foreach ($issues as $key => $issue) { + $issues[$key] = ' - '.$issue; + } + $issues = implode("\n", $issues); + + $overview = pht( + "Payment for this invoice could not be processed automatically:\n\n". + "%s", + $issues); + + $body->addRemarkupSection($overview); + + $body->addLinkSection( + pht('PAY NOW'), + PhabricatorEnv::getProductionURI($object->getCheckoutURI())); + } + $items = array(); foreach ($object->getPurchases() as $purchase) { $name = $purchase->getFullDisplayName(); @@ -123,9 +162,26 @@ $body->addTextSection(pht('ORDER CONTENTS'), implode("\n", $items)); + if ($this->isInvoice()) { + $subscription = id(new PhortuneSubscriptionQuery()) + ->setViewer($this->requireActor()) + ->withPHIDs(array($object->getSubscriptionPHID())) + ->executeOne(); + if ($subscription) { + $body->addLinkSection( + pht('SUBSCRIPTION'), + PhabricatorEnv::getProductionURI($subscription->getURI())); + } + } else { + $body->addLinkSection( + pht('ORDER DETAIL'), + PhabricatorEnv::getProductionURI($object->getDetailURI())); + } + + $account_uri = '/phortune/'.$object->getAccount()->getID().'/'; $body->addLinkSection( - pht('ORDER DETAIL'), - PhabricatorEnv::getProductionURI('/phortune/cart/'.$object->getID().'/')); + pht('ACCOUNT OVERVIEW'), + PhabricatorEnv::getProductionURI($account_uri)); return $body; } @@ -156,7 +212,7 @@ } protected function getMailSubjectPrefix() { - return 'Order'; + return '[Phortune]'; } protected function buildReplyHandler(PhabricatorLiskDAO $object) { diff --git a/src/applications/phortune/storage/PhortuneCartTransaction.php b/src/applications/phortune/storage/PhortuneCartTransaction.php --- a/src/applications/phortune/storage/PhortuneCartTransaction.php +++ b/src/applications/phortune/storage/PhortuneCartTransaction.php @@ -9,6 +9,7 @@ const TYPE_CANCEL = 'cart:cancel'; const TYPE_REFUND = 'cart:refund'; const TYPE_PURCHASED = 'cart:purchased'; + const TYPE_INVOICED = 'cart:invoiced'; public function getApplicationName() { return 'phortune'; @@ -49,9 +50,42 @@ return pht('This order was refunded.'); case self::TYPE_PURCHASED: return pht('Payment for this order was completed.'); + case self::TYPE_INVOICED: + return pht('This order was invoiced.'); } return parent::getTitle(); } + public function getTitleForMail() { + switch ($this->getTransactionType()) { + case self::TYPE_INVOICED: + return pht('You have a new invoice due.'); + } + + return parent::getTitleForMail(); + } + + public function getActionName() { + switch ($this->getTransactionType()) { + case self::TYPE_CREATED: + return pht('Created'); + case self::TYPE_HOLD: + return pht('Hold'); + case self::TYPE_REVIEW: + return pht('Review'); + case self::TYPE_CANCEL: + return pht('Cancelled'); + case self::TYPE_REFUND: + return pht('Refunded'); + case self::TYPE_PURCHASED: + return pht('Complete'); + case self::TYPE_INVOICED: + return pht('New Invoice'); + } + + return parent::getActionName(); + } + + } diff --git a/src/applications/phortune/subscription/PhortuneSubscriptionImplementation.php b/src/applications/phortune/subscription/PhortuneSubscriptionImplementation.php --- a/src/applications/phortune/subscription/PhortuneSubscriptionImplementation.php +++ b/src/applications/phortune/subscription/PhortuneSubscriptionImplementation.php @@ -22,12 +22,6 @@ $start_epoch, $end_epoch); - protected function getContentSource() { - return PhabricatorContentSource::newForSource( - PhabricatorContentSource::SOURCE_PHORTUNE, - array()); - } - public function getCartName( PhortuneSubscription $subscription, PhortuneCart $cart) { diff --git a/src/applications/phortune/worker/PhortuneSubscriptionWorker.php b/src/applications/phortune/worker/PhortuneSubscriptionWorker.php --- a/src/applications/phortune/worker/PhortuneSubscriptionWorker.php +++ b/src/applications/phortune/worker/PhortuneSubscriptionWorker.php @@ -73,9 +73,27 @@ return; } - // TODO: Send an email telling the user that we weren't able to autopay - // so they need to pay this manually. - throw new Exception(implode("\n", $issues)); + // We're shoving this through the CartEditor because it has all the logic + // for sending mail about carts. This doesn't really affect the state of + // the cart, but reduces the amount of code duplication. + + $xactions = array(); + $xactions[] = id(new PhortuneCartTransaction()) + ->setTransactionType(PhortuneCartTransaction::TYPE_INVOICED) + ->setNewValue(true); + + $content_source = PhabricatorContentSource::newForSource( + PhabricatorContentSource::SOURCE_PHORTUNE, + array()); + + $acting_phid = id(new PhabricatorPhortuneApplication())->getPHID(); + $editor = id(new PhortuneCartEditor()) + ->setActor($viewer) + ->setActingAsPHID($acting_phid) + ->setContentSource($content_source) + ->setContinueOnMissingFields(true) + ->setInvoiceIssues($issues) + ->applyTransactions($cart, $xactions); }