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