Ejegg has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/243360

Change subject: WIP Back-end logic for Amazon recurring donations
......................................................................

WIP Back-end logic for Amazon recurring donations

Creates the billing agreement that lets us charge monthly and makes
an initial charge against it.

TODO: set recurring parameters in the queue message.  Totally boggled
by the absence of subscr_id in the whole DI codebase

Bug: T111430
Change-Id: I41001359e413839cde6e0dcec84900c848d37b29
---
M amazon_gateway/amazon.adapter.php
M amazon_gateway/amazon.api.php
2 files changed, 158 insertions(+), 77 deletions(-)


  git pull 
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/DonationInterface 
refs/changes/60/243360/1

diff --git a/amazon_gateway/amazon.adapter.php 
b/amazon_gateway/amazon.adapter.php
index 775fac0..ea8eff3 100644
--- a/amazon_gateway/amazon.adapter.php
+++ b/amazon_gateway/amazon.adapter.php
@@ -156,8 +156,13 @@
                }
 
                try {
-                       $this->confirmOrderReference();
-                       $this->authorizeAndCapturePayment();
+                       if ( $this->getData_Unstaged_Escaped( 'recurring' ) === 
'1' ) {
+                               $this->confirmBillingAgreement();
+                               $this->authorizeAndCapturePayment( true );
+                       } else {
+                               $this->confirmOrderReference();
+                               $this->authorizeAndCapturePayment( false );
+                       }
                } catch ( ResponseProcessingException $ex ) {
                        $this->handleErrors( $ex, $this->transaction_response );
                }
@@ -213,34 +218,9 @@
                return $result;
        }
 
-       /**
-        * Amazon's widget has made calls to create an order reference object 
and
-        * has provided us the ID.  We make one API call to set amount, 
currency,
-        * and our note and local reference ID.  A second call confirms that the
-        * details are valid and moves it out of draft state.  Once it is out of
-        * draft state, we can retrieve the donor's name and email address with 
a
-        * third API call.
-        */
-       protected function confirmOrderReference() {
-               $orderReferenceId = $this->getData_Staged( 'order_reference_id' 
);
-
-               $this->setOrderReferenceDetailsIfUnset( $orderReferenceId );
-
-               $this->logger->info( "Confirming order $orderReferenceId" );
-               $this->callPwaClient( 'confirmOrderReference', array(
-                       'amazon_order_reference_id' => $orderReferenceId,
-               ) );
-
-               // TODO: either check the status, or skip this call when we 
already have
-               // donor details
-               $this->logger->info( "Getting details of order 
$orderReferenceId" );
-               $getDetailsResult = $this->callPwaClient( 
'getOrderReferenceDetails', array(
-                       'amazon_order_reference_id' => $orderReferenceId,
-               ) );
-
-               $buyerDetails = 
$getDetailsResult['GetOrderReferenceDetailsResult']['OrderReferenceDetails']['Buyer'];
-               $email = $buyerDetails['Email'];
-               $name = $buyerDetails['Name'];
+       protected function addDonorDetails( $donorDetails ) {
+               $email = $donorDetails['Email'];
+               $name = $donorDetails['Name'];
                $nameParts = preg_split( '/\s+/', $name, 2 ); // 
janky_split_name
                $fname = $nameParts[0];
                $lname = isset( $nameParts[1] ) ? $nameParts[1] : '';
@@ -257,53 +237,20 @@
        }
 
        /**
-        * Set the order reference details if they haven't been set yet.  Track
-        * which ones have been set in session.
-        * @param string $orderReferenceId
-        */
-       protected function setOrderReferenceDetailsIfUnset( $orderReferenceId ) 
{
-               if ( $this->session_getData( 'order_refs', $orderReferenceId ) 
) {
-                       return;
-               }
-               $this->logger->info( "Setting details for order 
$orderReferenceId" );
-               $this->callPwaClient( 'setOrderReferenceDetails', array(
-                       'amazon_order_reference_id' => $orderReferenceId,
-                       'amount' => $this->getData_Staged( 'amount' ),
-                       'currency_code' => $this->getData_Staged( 
'currency_code' ),
-                       'seller_note' => WmfFramework::formatMessage( 
'donate_interface-donation-description' ),
-                       'seller_order_reference_id' => $this->getData_Staged( 
'order_id' ),
-               ) );
-               // TODO: session_setData wrapper?
-               $_SESSION['order_refs'][$orderReferenceId] = true;
-       }
-
-       /**
-        * Once the order reference is finalized, we can authorize a payment 
against
-        * it and capture the funds.  We combine both steps in a single 
authorize
-        * call.  If the authorization is successful, we can check on the 
capture
-        * status and close the order reference.  TODO: determine if capture 
status
+        * Once the order reference or billing agreement is finalized, we can
+        * authorize a payment against it and capture the funds.  We combine 
both
+        * steps in a single authorize call.  If the authorization is 
successful,
+        * we can check on the capture status.  TODO: determine if capture 
status
         * check is really needed.  According to our tech contact, Amazon 
guarantees
         * that the capture will eventually succeed if the authorization 
succeeds.
         */
-       protected function authorizeAndCapturePayment() {
-               $orderReferenceId = $this->getData_Staged( 'order_reference_id' 
);
+       protected function authorizeAndCapturePayment( $recurring = false ) {
+               if ( $recurring ) {
+                       $authDetails = $this->authorizeOnBillingAgreement();
+               } else {
+                       $authDetails = $this->authorizeOnOrderReference();
+               }
 
-               $this->logger->info( "Authorizing and capturing payment on 
order $orderReferenceId" );
-               $authResponse = $this->callPwaClient( 'authorize', array(
-                       'amazon_order_reference_id' => $orderReferenceId,
-                       'authorization_amount' => $this->getData_Staged( 
'amount' ),
-                       'currency_code' => $this->getData_Staged( 
'currency_code' ),
-                       'capture_now' => true, // combine authorize and capture 
steps
-                       'authorization_reference_id' => $this->getData_Staged( 
'order_id' ),
-                       'transaction_timeout' => 0, // authorize synchronously
-                       // Could set 'SoftDescriptor' to control what appears 
on CC statement (16 char max, prepended with AMZ*)
-                       // Use the seller_authorization_note to simulate an 
error in the sandbox
-                       // See 
https://payments.amazon.com/documentation/lpwa/201749840#201750790
-                       // 'seller_authorization_note' => 
'{"SandboxSimulation": {"State":"Declined", 
"ReasonCode":"TransactionTimedOut"}}',
-                       // 'seller_authorization_note' => 
'{"SandboxSimulation": {"State":"Declined", 
"ReasonCode":"InvalidPaymentMethod"}}',
-               ) );
-
-               $authDetails = 
$authResponse['AuthorizeResult']['AuthorizationDetails'];
                if ( $authDetails['AuthorizationStatus']['State'] === 
'Declined' ) {
                        throw new ResponseProcessingException(
                                WmfFramework::formatMessage( 
'php-response-declined' ), // php- ??
@@ -335,6 +282,131 @@
        }
 
        /**
+        * Amazon's widget has made calls to create an order reference object 
and
+        * has provided us the ID.  We make one API call to set amount, 
currency,
+        * and our note and local reference ID.  A second call confirms that the
+        * details are valid and moves it out of draft state.  Once it is out of
+        * draft state, we can retrieve the donor's name and email address with 
a
+        * third API call.
+        */
+       protected function confirmOrderReference() {
+               $orderReferenceId = $this->getData_Staged( 'order_reference_id' 
);
+
+               $this->setOrderReferenceDetailsIfUnset( $orderReferenceId );
+
+               $this->logger->info( "Confirming order $orderReferenceId" );
+               $this->callPwaClient( 'confirmOrderReference', array(
+                       'amazon_order_reference_id' => $orderReferenceId,
+               ) );
+
+               // TODO: either check the status, or skip this call when we 
already have
+               // donor details
+               $this->logger->info( "Getting details of order 
$orderReferenceId" );
+               $getDetailsResult = $this->callPwaClient( 
'getOrderReferenceDetails', array(
+                       'amazon_order_reference_id' => $orderReferenceId,
+               ) );
+
+               $this->addDonorDetails(
+                       
$getDetailsResult['GetOrderReferenceDetailsResult']['OrderReferenceDetails']['Buyer']
+               );
+       }
+
+       /**
+        * Set the order reference details if they haven't been set yet.  Track
+        * which ones have been set in session.
+        * @param string $orderReferenceId
+        */
+       protected function setOrderReferenceDetailsIfUnset( $orderReferenceId ) 
{
+               if ( $this->session_getData( 'order_refs', $orderReferenceId ) 
) {
+                       return;
+               }
+               $this->logger->info( "Setting details for order 
$orderReferenceId" );
+               $this->callPwaClient( 'setOrderReferenceDetails', array(
+                       'amazon_order_reference_id' => $orderReferenceId,
+                       'amount' => $this->getData_Staged( 'amount' ),
+                       'currency_code' => $this->getData_Staged( 
'currency_code' ),
+                       'seller_note' => WmfFramework::formatMessage( 
'donate_interface-donation-description' ),
+                       'seller_order_reference_id' => $this->getData_Staged( 
'order_id' ),
+               ) );
+               // TODO: session_setData wrapper?
+               $_SESSION['order_refs'][$orderReferenceId] = true;
+       }
+
+       protected function authorizeOnOrderReference() {
+               $orderReferenceId = $this->getData_Staged( 'order_reference_id' 
);
+
+               $this->logger->info( "Authorizing and capturing payment on 
order $orderReferenceId" );
+               $authResponse = $this->callPwaClient( 'authorize', array(
+                       'amazon_order_reference_id' => $orderReferenceId,
+                       'authorization_amount' => $this->getData_Staged( 
'amount' ),
+                       'currency_code' => $this->getData_Staged( 
'currency_code' ),
+                       'capture_now' => true, // combine authorize and capture 
steps
+                       'authorization_reference_id' => $this->getData_Staged( 
'order_id' ),
+                       'transaction_timeout' => 0, // authorize synchronously
+                       // Could set 'SoftDescriptor' to control what appears 
on CC statement (16 char max, prepended with AMZ*)
+                       // Use the seller_authorization_note to simulate an 
error in the sandbox
+                       // See 
https://payments.amazon.com/documentation/lpwa/201749840#201750790
+                       // 'seller_authorization_note' => 
'{"SandboxSimulation": {"State":"Declined", 
"ReasonCode":"TransactionTimedOut"}}',
+                       // 'seller_authorization_note' => 
'{"SandboxSimulation": {"State":"Declined", 
"ReasonCode":"InvalidPaymentMethod"}}',
+               ) );
+               return $authResponse['AuthorizeResult']['AuthorizationDetails'];
+       }
+
+       protected function confirmBillingAgreement() {
+               $billingAgreementId = $this->getData_Staged( 
'billing_agreement_id' );
+               $this->setBillingAgreementDetailsIfUnset( $billingAgreementId );
+
+               $this->logger->info( "Confirming billing agreement 
$billingAgreementId" );
+               $this->callPwaClient( 'confirmBillingAgreement', array(
+                       'amazon_billing_agreement_id' => $billingAgreementId,
+               ) );
+
+               $this->logger->info( "Getting details of billing agreement 
$billingAgreementId" );
+               $getDetailsResult = $this->callPwaClient( 
'getBillingAgreementDetails', array(
+                       'amazon_billing_agreement_id' => $billingAgreementId,
+               ) );
+
+               $this->addDonorDetails(
+                       
$getDetailsResult['GetBillingAgreementDetailsResult']['BillingAgreementDetails']['Buyer']
+               );
+       }
+
+       protected function setBillingAgreementDetailsIfUnset( 
$billingAgreementId ) {
+               if ( $this->session_getData( 'billing_agreements', 
$billingAgreementId ) ) {
+                       return;
+               }
+               $this->logger->info( "Setting details for billing agreement 
$billingAgreementId" );
+               $this->callPwaClient( 'setBillingAgreementDetails', array(
+                       'amazon_billing_agreement_id' => $billingAgreementId,
+                       'seller_note' => WmfFramework::formatMessage( 
'donate_interface-monthly-donation-description' ),
+                       'seller_billing_agreement_id' => $this->getData_Staged( 
'order_id' ),
+               ) );
+               $_SESSION['billing_agreements'][$billingAgreementId] = true;
+       }
+
+       protected function authorizeOnBillingAgreement() {
+               $billingAgreementId = $this->getData_Staged( 
'billing_agreement_id' );
+
+               $this->logger->info( "Authorizing and capturing payment on 
billing agreement $billingAgreementId" );
+               $authResponse = $this->callPwaClient( 
'authorizeOnBillingAgreement', array(
+                       'amazon_billing_agreement_id' => $billingAgreementId,
+                       'authorization_amount' => $this->getData_Staged( 
'amount' ),
+                       'currency_code' => $this->getData_Staged( 
'currency_code' ),
+                       'capture_now' => true, // combine authorize and capture 
steps
+                       'authorization_reference_id' => $this->getData_Staged( 
'order_id' ),
+                       'seller_order_id' => $this->getData_Staged( 'order_id' 
),
+                       'seller_note' => WmfFramework::formatMessage( 
'donate_interface-monthly-donation-description' ),
+                       'transaction_timeout' => 0, // authorize synchronously
+                       // Could set 'SoftDescriptor' to control what appears 
on CC statement (16 char max, prepended with AMZ*)
+                       // Use the seller_authorization_note to simulate an 
error in the sandbox
+                       // See 
https://payments.amazon.com/documentation/lpwa/201749840#201750790
+                       // 'seller_authorization_note' => 
'{"SandboxSimulation": {"State":"Declined", 
"ReasonCode":"TransactionTimedOut"}}',
+                       // 'seller_authorization_note' => 
'{"SandboxSimulation": {"State":"Declined", 
"ReasonCode":"InvalidPaymentMethod"}}',
+               ) );
+               return 
$authResponse['AuthorizeOnBillingAgreementResult']['AuthorizationDetails'];
+       }
+
+       /**
         * Replace decimal point with a dash to comply with Amazon's 
restrictions on
         * seller reference ID format.
         */
diff --git a/amazon_gateway/amazon.api.php b/amazon_gateway/amazon.api.php
index 73cdc2d..33ebd84 100644
--- a/amazon_gateway/amazon.api.php
+++ b/amazon_gateway/amazon.api.php
@@ -3,19 +3,22 @@
 class AmazonBillingApi extends ApiBase {
        protected $allowedParams = array(
                'amount',
+               'billingAgreementId',
                'currency_code',
                'orderReferenceId',
+               'recurring',
                'token',
        );
 
        public function execute() {
                $output = $this->getResult();
-               $orderReferenceId = $this->getParameter( 'orderReferenceId' );
+               $recurring = $this->getParameter( 'recurring');
                $adapterParams = array(
                        'api_request' => true,
                        'external_data' => array(
                                'amount' => $this->getParameter( 'amount' ),
                                'currency_code' => $this->getParameter( 
'currency_code' ),
+                               'recurring' => $this->getParameter( 'recurring' 
),
                        ),
                );
 
@@ -28,9 +31,15 @@
                                $adapter->getValidationErrors()
                        );
                } else if ( $adapter->checkTokens() ) {
-                       $adapter->addRequestData( array(
-                               'order_reference_id' => $orderReferenceId,
-                       ) );
+                       if ( $recurring ) {
+                               $adapter->addRequestData( array(
+                                       'billing_agreement_id' => 
$this->getParameter( 'billingAgreementId' ),
+                               ) );
+                       } else {
+                               $adapter->addRequestData( array(
+                                       'order_reference_id' => 
$this->getParameter( 'orderReferenceId' ),
+                               ) );
+                       }
                        $result = $adapter->doPayment();
                        if ( $result->isFailed() ) {
                                $output->addvalue(

-- 
To view, visit https://gerrit.wikimedia.org/r/243360
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I41001359e413839cde6e0dcec84900c848d37b29
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/DonationInterface
Gerrit-Branch: master
Gerrit-Owner: Ejegg <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to