jenkins-bot has submitted this change and it was merged.
Change subject: Back-end logic for Amazon recurring donations
......................................................................
Back-end logic for Amazon recurring donations
Creates the billing agreement that lets us charge monthly and makes
an initial charge against it.
Adds optional subscr_id to queue messages sent from payments frontend,
since you can't derive the billing agreement ID from the capture ID
we record as gateway_txn_id.
Bug: T111430
Change-Id: I41001359e413839cde6e0dcec84900c848d37b29
---
M amazon_gateway/amazon.adapter.php
M amazon_gateway/amazon.api.php
M gateway_common/DonationData.php
M gateway_common/DonationQueue.php
M tests/Adapter/Amazon/AmazonTest.php
M tests/includes/MockAmazonClient.php
A tests/includes/Responses/amazon/authorizeOnBillingAgreement.json
A tests/includes/Responses/amazon/confirmBillingAgreement.json
A tests/includes/Responses/amazon/getBillingAgreementDetails.json
A tests/includes/Responses/amazon/setBillingAgreementDetails.json
10 files changed, 310 insertions(+), 79 deletions(-)
Approvals:
Awight: Looks good to me, approved
jenkins-bot: Verified
diff --git a/amazon_gateway/amazon.adapter.php
b/amazon_gateway/amazon.adapter.php
index 775fac0..1742d4b 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,127 @@
}
/**
+ * 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( 'subscr_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( 'subscr_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
+ // '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..f0eb4aa 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(
+ 'subscr_id' => $this->getParameter(
'billingAgreementId' ),
+ ) );
+ } else {
+ $adapter->addRequestData( array(
+ 'order_reference_id' =>
$this->getParameter( 'orderReferenceId' ),
+ ) );
+ }
$result = $adapter->doPayment();
if ( $result->isFailed() ) {
$output->addvalue(
diff --git a/gateway_common/DonationData.php b/gateway_common/DonationData.php
index 6657922..5a9d943 100644
--- a/gateway_common/DonationData.php
+++ b/gateway_common/DonationData.php
@@ -95,6 +95,7 @@
'submethod', //same as above. Ideally, the
newer banners would stop using these vars and go back to the old ones...
'issuer_id',
'order_id',
+ 'subscr_id',
'referrer',
'utm_source',
'utm_source_id',
@@ -915,6 +916,7 @@
'gateway',
'gateway_account',
'gateway_txn_id',
+ 'subscr_id',
'recurring',
'payment_method',
'payment_submethod',
diff --git a/gateway_common/DonationQueue.php b/gateway_common/DonationQueue.php
index f47edb1..cdb203a 100644
--- a/gateway_common/DonationQueue.php
+++ b/gateway_common/DonationQueue.php
@@ -229,6 +229,7 @@
'state_province' => 'state',
'street_address' => 'street',
'supplemental_address_1' => 'street_supplemental',
+ 'subscr_id' => 'subscr_id',
'utm_campaign' => 'utm_campaign',
'utm_medium' => 'utm_medium',
'postal_code' => 'zip',
diff --git a/tests/Adapter/Amazon/AmazonTest.php
b/tests/Adapter/Amazon/AmazonTest.php
index 1e47950..3e439a0 100644
--- a/tests/Adapter/Amazon/AmazonTest.php
+++ b/tests/Adapter/Amazon/AmazonTest.php
@@ -256,4 +256,38 @@
$errors = $result->getErrors();
$this->assertTrue( isset( $errors[ResponseCodes::NO_RESPONSE]
), 'NO_RESPONSE error should be set' );
}
+
+ /**
+ * Check the adapter makes the correct calls for successful monthly
donations
+ */
+ function testDoRecurringPaymentSuccess() {
+ $init = $this->getDonorTestData( 'US' );
+ $init['amount'] = '10.00';
+ $init['recurring'] = '1';
+ $init['subscr_id'] = 'C01-9650293-7351908';
+ // We don't get any profile data up front
+ unset( $init['email'] );
+ unset( $init['fname'] );
+ unset( $init['lname'] );
+
+ $gateway = $this->getFreshGatewayObject( $init );
+ $result = $gateway->doPayment();
+ // FIXME: PaymentResult->isFailed returns null for false
+ $this->assertTrue( !( $result->isFailed() ), 'Result should not
be failed when responses are good' );
+ $this->assertEquals( 'Testy',
$gateway->getData_Unstaged_Escaped( 'fname' ), 'Did not populate first name
from Amazon data' );
+ $this->assertEquals( 'Test',
$gateway->getData_Unstaged_Escaped( 'lname' ), 'Did not populate last name from
Amazon data' );
+ $this->assertEquals( '[email protected]',
$gateway->getData_Unstaged_Escaped( 'email' ), 'Did not populate email from
Amazon data' );
+ $mockClient = TestingAmazonAdapter::$mockClient;
+ $setBillingAgreementDetailsArgs =
$mockClient->calls['setBillingAgreementDetails'][0];
+ $oid = $gateway->getData_Unstaged_Escaped( 'order_id' );
+ $this->assertEquals( $oid,
$setBillingAgreementDetailsArgs['seller_billing_agreement_id'], 'Did not set
order id on billing agreement' );
+ $authorizeOnBillingAgreementDetailsArgs =
$mockClient->calls['authorizeOnBillingAgreement'][0];
+ $this->assertEquals( $init['amount'],
$authorizeOnBillingAgreementDetailsArgs['authorization_amount'], 'Did not
authorize correct amount' );
+ $this->assertEquals( $init['currency_code'],
$authorizeOnBillingAgreementDetailsArgs['currency_code'], 'Did not authorize
correct currency code' );
+ $queued = $gateway->queue_messages;
+ $this->assertNotEmpty( $queued['complete'], 'Not sending a
message to the complete queue' );
+ $message = $queued['complete'][0];
+ $this->assertEquals( 'S01-5318994-6362993-C004044',
$message['gateway_txn_id'], 'Queue message has wrong txn ID' );
+ $this->assertEquals( $init['subscr_id'], $message['subscr_id'],
'Queue message has wrong subscription ID' );
+ }
}
diff --git a/tests/includes/MockAmazonClient.php
b/tests/includes/MockAmazonClient.php
index cdeec61..6915609 100644
--- a/tests/includes/MockAmazonClient.php
+++ b/tests/includes/MockAmazonClient.php
@@ -49,7 +49,7 @@
}
public function authorizeOnBillingAgreement( $requestParameters =
array() ) {
-
+ return $this->fakeCall( 'authorizeOnBillingAgreement',
$requestParameters );
}
public function cancelOrderReference( $requestParameters = array() ) {
@@ -77,7 +77,7 @@
}
public function confirmBillingAgreement( $requestParameters = array() )
{
-
+ return $this->fakeCall( 'confirmBillingAgreement',
$requestParameters );
}
public function confirmOrderReference( $requestParameters = array() ) {
@@ -137,6 +137,7 @@
}
public function setBillingAgreementDetails( $requestParameters =
array() ) {
+ return $this->fakeCall( 'setBillingAgreementDetails',
$requestParameters );
}
diff --git a/tests/includes/Responses/amazon/authorizeOnBillingAgreement.json
b/tests/includes/Responses/amazon/authorizeOnBillingAgreement.json
new file mode 100644
index 0000000..0163e64
--- /dev/null
+++ b/tests/includes/Responses/amazon/authorizeOnBillingAgreement.json
@@ -0,0 +1,38 @@
+{
+ "AuthorizeOnBillingAgreementResult": {
+ "AuthorizationDetails": {
+ "AuthorizationAmount": {
+ "CurrencyCode": "USD",
+ "Amount": "10.00"
+ },
+ "CapturedAmount": {
+ "CurrencyCode": "USD",
+ "Amount": "0"
+ },
+ "SoftDescriptor": "AMZ*Wikimedia Founda",
+ "ExpirationTimestamp": "2015-11-04T18:06:49.965Z",
+ "IdList": {
+ "member": "S01-5318994-6362993-C004044"
+ },
+ "AuthorizationStatus": {
+ "LastUpdateTimestamp": "2015-10-05T18:06:49.965Z",
+ "State": "Closed",
+ "ReasonCode": "MaxCapturesProcessed"
+ },
+ "AuthorizationFee": {
+ "CurrencyCode": "USD",
+ "Amount": "0.00"
+ },
+ "CaptureNow": "true",
+ "CreationTimestamp": "2015-10-05T18:06:49.965Z",
+ "SellerAuthorizationNote": [],
+ "AmazonAuthorizationId": "S01-5318994-6362993-A004044",
+ "AuthorizationReferenceId": "36834-0"
+ },
+ "AmazonOrderReferenceId": "S01-5318994-6362993"
+ },
+ "ResponseMetadata": {
+ "RequestId": "8c8f4d79-06d5-4de5-ae34-d3fdc73aa343"
+ },
+ "ResponseStatus": "200"
+}
\ No newline at end of file
diff --git a/tests/includes/Responses/amazon/confirmBillingAgreement.json
b/tests/includes/Responses/amazon/confirmBillingAgreement.json
new file mode 100644
index 0000000..266dad3
--- /dev/null
+++ b/tests/includes/Responses/amazon/confirmBillingAgreement.json
@@ -0,0 +1,7 @@
+{
+ "ConfirmBillingAgreementResult": [],
+ "ResponseMetadata": {
+ "RequestId": "61e7b7c9-ed72-4a27-b18f-6ec37ac031c5"
+ },
+ "ResponseStatus": "200"
+}
\ No newline at end of file
diff --git a/tests/includes/Responses/amazon/getBillingAgreementDetails.json
b/tests/includes/Responses/amazon/getBillingAgreementDetails.json
new file mode 100644
index 0000000..0caa393
--- /dev/null
+++ b/tests/includes/Responses/amazon/getBillingAgreementDetails.json
@@ -0,0 +1,38 @@
+{
+ "GetBillingAgreementDetailsResult": {
+ "BillingAgreementDetails": {
+ "BillingAgreementStatus": {
+ "LastUpdatedTimestamp": "2015-10-05T18:06:48.288Z",
+ "State": "Open"
+ },
+ "AmazonBillingAgreementId": "C01-9650293-7351908",
+ "BillingAgreementConsent": "true",
+ "SellerBillingAgreementAttributes": {
+ "SellerBillingAgreementId": "36834-0"
+ },
+ "Buyer": {
+ "Name": "Testy Test",
+ "Email": "[email protected]"
+ },
+ "ReleaseEnvironment": "Sandbox",
+ "SellerNote": "Monthly donation to the Wikimedia Foundation",
+ "CreationTimestamp": "2015-10-05T18:06:38.940Z",
+ "BillingAgreementLimits": {
+ "TimePeriodStartDate": "2015-10-01T00:00:00Z",
+ "CurrentRemainingBalance": {
+ "CurrencyCode": "USD",
+ "Amount": "500.00"
+ },
+ "AmountLimitPerTimePeriod": {
+ "CurrencyCode": "USD",
+ "Amount": "500"
+ },
+ "TimePeriodEndDate": "2015-11-01T00:00:00Z"
+ }
+ }
+ },
+ "ResponseMetadata": {
+ "RequestId": "547cb94e-690e-47e9-99cd-aef79622a451"
+ },
+ "ResponseStatus": "200"
+}
\ No newline at end of file
diff --git a/tests/includes/Responses/amazon/setBillingAgreementDetails.json
b/tests/includes/Responses/amazon/setBillingAgreementDetails.json
new file mode 100644
index 0000000..85aac83
--- /dev/null
+++ b/tests/includes/Responses/amazon/setBillingAgreementDetails.json
@@ -0,0 +1,33 @@
+{
+ "SetBillingAgreementDetailsResult": {
+ "BillingAgreementDetails": {
+ "AmazonBillingAgreementId": "C01-9650293-7351908",
+ "BillingAgreementStatus": {
+ "State": "Draft"
+ },
+ "BillingAgreementConsent": "true",
+ "SellerBillingAgreementAttributes": {
+ "SellerBillingAgreementId": "36834-0"
+ },
+ "ReleaseEnvironment": "Sandbox",
+ "SellerNote": "Monthly donation to the Wikimedia Foundation",
+ "CreationTimestamp": "2015-10-05T18:06:38.940Z",
+ "BillingAgreementLimits": {
+ "TimePeriodStartDate": "2015-10-01T00:00:00Z",
+ "CurrentRemainingBalance": {
+ "CurrencyCode": "USD",
+ "Amount": "500.00"
+ },
+ "AmountLimitPerTimePeriod": {
+ "CurrencyCode": "USD",
+ "Amount": "500"
+ },
+ "TimePeriodEndDate": "2015-11-01T00:00:00Z"
+ }
+ }
+ },
+ "ResponseMetadata": {
+ "RequestId": "9a92b3f8-d8d2-46bf-b998-e3332c71df61"
+ },
+ "ResponseStatus": "200"
+}
\ No newline at end of file
--
To view, visit https://gerrit.wikimedia.org/r/243360
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I41001359e413839cde6e0dcec84900c848d37b29
Gerrit-PatchSet: 4
Gerrit-Project: mediawiki/extensions/DonationInterface
Gerrit-Branch: master
Gerrit-Owner: Ejegg <[email protected]>
Gerrit-Reviewer: AndyRussG <[email protected]>
Gerrit-Reviewer: Awight <[email protected]>
Gerrit-Reviewer: Cdentinger <[email protected]>
Gerrit-Reviewer: Ssmith <[email protected]>
Gerrit-Reviewer: XenoRyet <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits