Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/phortune/controller/PhortuneProviderController.php b/src/applications/phortune/controller/PhortuneProviderController.php
index 96d2bc4157..be66ca695f 100644
--- a/src/applications/phortune/controller/PhortuneProviderController.php
+++ b/src/applications/phortune/controller/PhortuneProviderController.php
@@ -1,74 +1,88 @@
<?php
final class PhortuneProviderController extends PhortuneController {
private $digest;
private $action;
public function willProcessRequest(array $data) {
$this->digest = $data['digest'];
$this->setAction($data['action']);
}
public function setAction($action) {
$this->action = $action;
return $this;
}
public function getAction() {
return $this->action;
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
// NOTE: This use of digests to identify payment providers is because
// payment provider keys don't necessarily have restrictions on what they
// contain (so they might have stuff that's not safe to put in URIs), and
// using digests prevents errors with URI encoding.
$provider = PhortunePaymentProvider::getProviderByDigest($this->digest);
if (!$provider) {
throw new Exception('Invalid payment provider digest!');
}
if (!$provider->canRespondToControllerAction($this->getAction())) {
return new Aphront404Response();
}
$response = $provider->processControllerRequest($this, $request);
if ($response instanceof AphrontResponse) {
return $response;
}
$title = 'Phortune';
return $this->buildApplicationPage(
$response,
array(
'title' => $title,
));
}
public function loadCart($id) {
$request = $this->getRequest();
$viewer = $request->getUser();
return id(new PhortuneCartQuery())
->setViewer($viewer)
->needPurchases(true)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
}
+ public function loadActiveCharge(PhortuneCart $cart) {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ return id(new PhortuneChargeQuery())
+ ->setViewer($viewer)
+ ->withCartPHIDs(array($cart->getPHID()))
+ ->withStatuses(
+ array(
+ PhortuneCharge::STATUS_CHARGING,
+ ))
+ ->executeOne();
+ }
+
}
diff --git a/src/applications/phortune/provider/PhortunePaypalPaymentProvider.php b/src/applications/phortune/provider/PhortunePaypalPaymentProvider.php
index 21af366997..44af747ea9 100644
--- a/src/applications/phortune/provider/PhortunePaypalPaymentProvider.php
+++ b/src/applications/phortune/provider/PhortunePaypalPaymentProvider.php
@@ -1,141 +1,225 @@
<?php
final class PhortunePaypalPaymentProvider extends PhortunePaymentProvider {
public function isEnabled() {
+ // TODO: See note in processControllerRequest().
+ return false;
+
return $this->getPaypalAPIUsername() &&
$this->getPaypalAPIPassword() &&
$this->getPaypalAPISignature();
}
public function getProviderType() {
return 'paypal';
}
public function getProviderDomain() {
return 'paypal.com';
}
public function getPaymentMethodDescription() {
return pht('Credit Card or Paypal Account');
}
public function getPaymentMethodIcon() {
return celerity_get_resource_uri('rsrc/image/phortune/paypal.png');
}
public function getPaymentMethodProviderDescription() {
return 'Paypal';
}
public function canHandlePaymentMethod(PhortunePaymentMethod $method) {
$type = $method->getMetadataValue('type');
return ($type == 'paypal');
}
protected function executeCharge(
PhortunePaymentMethod $payment_method,
PhortuneCharge $charge) {
throw new Exception('!');
}
private function getPaypalAPIUsername() {
return PhabricatorEnv::getEnvConfig('phortune.paypal.api-username');
}
private function getPaypalAPIPassword() {
return PhabricatorEnv::getEnvConfig('phortune.paypal.api-password');
}
private function getPaypalAPISignature() {
return PhabricatorEnv::getEnvConfig('phortune.paypal.api-signature');
}
/* -( One-Time Payments )-------------------------------------------------- */
public function canProcessOneTimePayments() {
return true;
}
/* -( Controllers )-------------------------------------------------------- */
public function canRespondToControllerAction($action) {
switch ($action) {
case 'checkout':
case 'charge':
case 'cancel':
return true;
}
return parent::canRespondToControllerAction();
}
public function processControllerRequest(
PhortuneProviderController $controller,
AphrontRequest $request) {
+ $viewer = $request->getUser();
+
$cart = $controller->loadCart($request->getInt('cartID'));
if (!$cart) {
return new Aphront404Response();
}
+ $charge = $controller->loadActiveCharge($cart);
+ switch ($controller->getAction()) {
+ case 'checkout':
+ if ($charge) {
+ throw new Exception(pht('Cart is already charging!'));
+ }
+ break;
+ case 'charge':
+ case 'cancel':
+ if (!$charge) {
+ throw new Exception(pht('Cart is not charging yet!'));
+ }
+ break;
+ }
+
switch ($controller->getAction()) {
case 'checkout':
$return_uri = $this->getControllerURI(
'charge',
array(
'cartID' => $cart->getID(),
));
$cancel_uri = $this->getControllerURI(
'cancel',
array(
'cartID' => $cart->getID(),
));
$price = $cart->getTotalPriceAsCurrency();
+ $charge = $cart->willApplyCharge($viewer, $this);
+
+ $params = array(
+ 'PAYMENTREQUEST_0_AMT' => $price->formatBareValue(),
+ 'PAYMENTREQUEST_0_CURRENCYCODE' => $price->getCurrency(),
+ 'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',
+ 'PAYMENTREQUEST_0_CUSTOM' => $charge->getPHID(),
+
+ 'RETURNURL' => $return_uri,
+ 'CANCELURL' => $cancel_uri,
+
+ // TODO: This should be cart-dependent if we eventually support
+ // physical goods.
+ 'NOSHIPPING' => '1',
+ );
+
$result = $this
->newPaypalAPICall()
- ->setRawPayPalQuery(
- 'SetExpressCheckout',
- array(
- 'PAYMENTREQUEST_0_AMT' => $price->formatBareValue(),
- 'PAYMENTREQUEST_0_CURRENCYCODE' => $price->getCurrency(),
- 'RETURNURL' => $return_uri,
- 'CANCELURL' => $cancel_uri,
- 'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',
- ))
+ ->setRawPayPalQuery('SetExpressCheckout', $params)
->resolve();
$uri = new PhutilURI('https://www.sandbox.paypal.com/cgi-bin/webscr');
$uri->setQueryParams(
array(
'cmd' => '_express-checkout',
'token' => $result['TOKEN'],
));
+ $cart->setMetadataValue('provider.checkoutURI', $uri);
+ $cart->save();
+
+ $charge->setMetadataValue('paypal.token', $result['TOKEN']);
+ $charge->save();
+
return id(new AphrontRedirectResponse())
->setIsExternal(true)
->setURI($uri);
case 'charge':
- var_dump($_REQUEST);
+ $token = $request->getStr('token');
+
+ $params = array(
+ 'TOKEN' => $token,
+ );
+
+ $result = $this
+ ->newPaypalAPICall()
+ ->setRawPayPalQuery('GetExpressCheckoutDetails', $params)
+ ->resolve();
+
+ var_dump($result);
+
+ if ($result['CUSTOM'] !== $charge->getPHID()) {
+ throw new Exception(
+ pht('Paypal checkout does not match Phortune charge!'));
+ }
+
+ if ($result['CHECKOUTSTATUS'] !== 'PaymentActionNotInitiated') {
+ throw new Exception(
+ pht(
+ 'Expected status "%s", got "%s".',
+ 'PaymentActionNotInitiated',
+ $result['CHECKOUTSTATUS']));
+ }
+
+ $price = $cart->getTotalPriceAsCurrency();
+
+ $params = array(
+ 'TOKEN' => $token,
+ 'PAYERID' => $result['PAYERID'],
+
+ 'PAYMENTREQUEST_0_AMT' => $price->formatBareValue(),
+ 'PAYMENTREQUEST_0_CURRENCYCODE' => $price->getCurrency(),
+ 'PAYMENTREQUEST_0_PAYMENTACTION' => 'Sale',
+ );
+
+ $result = $this
+ ->newPaypalAPICall()
+ ->setRawPayPalQuery('DoExpressCheckoutPayment', $params)
+ ->resolve();
+
+ // TODO: Paypal can send requests back in "PaymentReview" status,
+ // and does this for test transactions. We're supposed to hold
+ // the transaction and poll the API every 6 hours. This is unreasonably
+ // difficult for now and we can't reasonably just fail these charges.
+
+ var_dump($result);
+
+ die();
break;
case 'cancel':
var_dump($_REQUEST);
break;
}
- throw new Exception("The rest of this isn't implemented yet.");
+ throw new Exception(
+ pht('Unsupported action "%s".', $controller->getAction()));
}
private function newPaypalAPICall() {
return id(new PhutilPayPalAPIFuture())
->setHost('https://api-3t.sandbox.paypal.com/nvp')
->setAPIUsername($this->getPaypalAPIUsername())
->setAPIPassword($this->getPaypalAPIPassword())
->setAPISignature($this->getPaypalAPISignature());
}
}
diff --git a/src/applications/phortune/provider/PhortuneWePayPaymentProvider.php b/src/applications/phortune/provider/PhortuneWePayPaymentProvider.php
index 0bfe334654..57dd2d6817 100644
--- a/src/applications/phortune/provider/PhortuneWePayPaymentProvider.php
+++ b/src/applications/phortune/provider/PhortuneWePayPaymentProvider.php
@@ -1,222 +1,214 @@
<?php
final class PhortuneWePayPaymentProvider extends PhortunePaymentProvider {
public function isEnabled() {
return $this->getWePayClientID() &&
$this->getWePayClientSecret() &&
$this->getWePayAccessToken() &&
$this->getWePayAccountID();
}
public function getProviderType() {
return 'wepay';
}
public function getProviderDomain() {
return 'wepay.com';
}
public function getPaymentMethodDescription() {
return pht('Credit Card or Bank Account');
}
public function getPaymentMethodIcon() {
return celerity_get_resource_uri('/rsrc/image/phortune/wepay.png');
}
public function getPaymentMethodProviderDescription() {
return 'WePay';
}
public function canHandlePaymentMethod(PhortunePaymentMethod $method) {
$type = $method->getMetadataValue('type');
return ($type == 'wepay');
}
protected function executeCharge(
PhortunePaymentMethod $payment_method,
PhortuneCharge $charge) {
throw new Exception('!');
}
private function getWePayClientID() {
return PhabricatorEnv::getEnvConfig('phortune.wepay.client-id');
}
private function getWePayClientSecret() {
return PhabricatorEnv::getEnvConfig('phortune.wepay.client-secret');
}
private function getWePayAccessToken() {
return PhabricatorEnv::getEnvConfig('phortune.wepay.access-token');
}
private function getWePayAccountID() {
return PhabricatorEnv::getEnvConfig('phortune.wepay.account-id');
}
/* -( One-Time Payments )-------------------------------------------------- */
public function canProcessOneTimePayments() {
return true;
}
/* -( Controllers )-------------------------------------------------------- */
public function canRespondToControllerAction($action) {
switch ($action) {
case 'checkout':
case 'charge':
case 'cancel':
return true;
}
return parent::canRespondToControllerAction();
}
/**
* @phutil-external-symbol class WePay
*/
public function processControllerRequest(
PhortuneProviderController $controller,
AphrontRequest $request) {
$viewer = $request->getUser();
$cart = $controller->loadCart($request->getInt('cartID'));
if (!$cart) {
return new Aphront404Response();
}
$root = dirname(phutil_get_library_root('phabricator'));
require_once $root.'/externals/wepay/wepay.php';
WePay::useStaging(
$this->getWePayClientID(),
$this->getWePayClientSecret());
$wepay = new WePay($this->getWePayAccessToken());
- $charge = id(new PhortuneChargeQuery())
- ->setViewer($viewer)
- ->withCartPHIDs(array($cart->getPHID()))
- ->withStatuses(
- array(
- PhortuneCharge::STATUS_CHARGING,
- ))
- ->executeOne();
-
+ $charge = $controller->loadActiveCharge($cart);
switch ($controller->getAction()) {
case 'checkout':
if ($charge) {
throw new Exception(pht('Cart is already charging!'));
}
break;
case 'charge':
case 'cancel':
if (!$charge) {
throw new Exception(pht('Cart is not charging yet!'));
}
break;
}
switch ($controller->getAction()) {
case 'checkout':
$return_uri = $this->getControllerURI(
'charge',
array(
'cartID' => $cart->getID(),
));
$cancel_uri = $this->getControllerURI(
'cancel',
array(
'cartID' => $cart->getID(),
));
$price = $cart->getTotalPriceAsCurrency();
$params = array(
'account_id' => $this->getWePayAccountID(),
'short_description' => 'Services', // TODO
'type' => 'SERVICE',
'amount' => $price->formatBareValue(),
'long_description' => 'Services', // TODO
'reference_id' => $cart->getPHID(),
'app_fee' => 0,
'fee_payer' => 'Payee',
'redirect_uri' => $return_uri,
'fallback_uri' => $cancel_uri,
// NOTE: If we don't `auto_capture`, we might get a result back in
// either an "authorized" or a "reserved" state. We can't capture
// an "authorized" result, so just autocapture.
'auto_capture' => true,
'require_shipping' => 0,
'shipping_fee' => 0,
'charge_tax' => 0,
'mode' => 'regular',
'funding_sources' => 'bank,cc'
);
$charge = $cart->willApplyCharge($viewer, $this);
$result = $wepay->request('checkout/create', $params);
$cart->setMetadataValue('provider.checkoutURI', $result->checkout_uri);
$cart->save();
$charge->setMetadataValue('wepay.checkoutID', $result->checkout_id);
$charge->save();
$uri = new PhutilURI($result->checkout_uri);
return id(new AphrontRedirectResponse())
->setIsExternal(true)
->setURI($uri);
case 'charge':
$checkout_id = $request->getInt('checkout_id');
$params = array(
'checkout_id' => $checkout_id,
);
$checkout = $wepay->request('checkout', $params);
if ($checkout->reference_id != $cart->getPHID()) {
throw new Exception(
pht('Checkout reference ID does not match cart PHID!'));
}
switch ($checkout->state) {
case 'authorized':
case 'reserved':
case 'captured':
break;
default:
throw new Exception(
pht(
'Checkout is in bad state "%s"!',
$result->state));
}
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$cart->didApplyCharge($charge);
unset($unguarded);
return id(new AphrontRedirectResponse())
->setURI($cart->getDoneURI());
case 'cancel':
// TODO: I don't know how it's possible to cancel out of a WePay
// charge workflow.
throw new Exception(
pht('How did you get here? WePay has no cancel flow in its UI...?'));
break;
}
throw new Exception(
pht('Unsupported action "%s".', $controller->getAction()));
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Nov 6, 6:57 AM (9 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
321719
Default Alt Text
(16 KB)

Event Timeline