Version 1 by Dan Bowen
on Dec 30, 2010 16:55.

compared with
Version 2 by Dan Bowen
on Dec 30, 2010 17:12.

Key
This line was removed.
This word was removed. This word was added.
This line was added.

Changes (32)

View Page History
{info:title=New Proposal Template}
This page has been created from a template that uses "zones." To proceed:

# Edit the page
# Replace sample content within each zone-data tag with your own content
# Remove this notice
# Save the page
# When you are ready for community review, move this page to the [Ready for Review] section on the edit page.

{note:title=No placeholders allowed!}
Please do not create placeholders. Wait until you have sufficient content to replace all sample data in the proposal template before creating your proposal document.
{note}
{info}

{zone-template-instance:ZFPROP:Proposal Zone Template}

{zone-data:component-name}
Zend_Magic Zend_Service_Chargify
{zone-data}

{zone-data:proposer-list}
[My Name|mailto:noreply@zend.com]
[Dan Bowen|mailto:dan@crucialwebstudio.com]
{zone-data}

{zone-data}

{zone-data:revision}
1.0 - 1 January 2008: 30 December 2010: Initial Draft.
{zone-data}

{zone-data:overview}
Zend_Magic is a simple component that reads my mind and generates code dynamically to do what I want.
Chargify.com is a subscription billing system that allows web application developers to charge customer credit cards on a recurring basis.
 
The Zend_Service_Chargify component makes it easy for developers to interact with the Chargify.com REST API for seamless integration into their website or application (as opposed to using hosted payment pages).
{zone-data}

{zone-data:references}
* [Harry Houdini Wikipedia Entry|http://en.wikipedia.org/wiki/Harry_Houdini]
* [MagicTricks.com|http://www.magictricks.com/]
* [Chargify API Documentation|http://docs.chargify.com/api-introduction]
{zone-data}

{zone-data:requirements}
Most requirements take the form of "foo will do ...." or "foo will not support ...", although different words and sentence structure might be used. Adding functionality to your proposal is requirements creep (bad), unless listed below. Discuss major changes with your team first, and then open a "feature improvement" issue against this component.

* This component *will* correctly reads a developers mind for intent and generate the right configuration file.
* The generated config file *will not* support XML, but will provide an extension point in the API.
* This component *will* use no more memory than twice the size of all data it contains.
* This component *will* include a factory method.
* This component *will not* allow subclassing. (i.e. when reviewed, we expect to see "final" keyword in code)
* This component *will* only generate data exports strictly complying with RFC 12345.
* This component *will* validate input data against formats supported by ZF component Foo.
* This component *will not* save any data using Zend_Cache or the filesystem. All transient data *will be* saved using Zend_Session.
The component *will* wrap the entire Chargify REST API.
The component *will* provide responses as Arrays.
The component *will* allow for both JSON and XML requests and responses.
The component *will* respond appropriately to HTTP response codes returned by the Chargify API
{zone-data}

{zone-data:dependencies}
* Zend_Exception Zend_Service_Abstract
* Zend_Http_Client
* Zend_Http_Response
* Zend_Json
{zone-data}

{zone-data:operation}
The component is instantiated with a mind-link that ...
The Application Developer has an account with Chargify.com
The Application Developer instantiates Zend_Service_Chargify with their API key & password (and possibly other options)
The Application Developer uses API functionality to create customers, create subscriptions and view transaction history (and more)
{zone-data}

{zone-data:milestones}
Describe some intermediate state of this component in terms of design notes, additional material added to this page, and / code. Note any significant dependencies here, such as, "Milestone #3 can not be completed until feature Foo has been added to ZF component XYZ." Milestones will be required for acceptance of future proposals. They are not hard, and many times you will only need to think of the first three below.
* Milestone 1: [design notes will be published here|http://framework.zend.com/wiki/x/sg]
* Milestone 2: Working prototype checked into the incubator supporting use cases #1, #2, ...
* Milestone 3: Working prototype checked into the incubator supporting use cases #3 and #4.
* Milestone 4: Unit tests exist, work, and are checked into SVN.
* Milestone 5: Initial documentation exists.

If a milestone is already done, begin the description with "\[DONE\]", like this:
* Milestone #: \[DONE\] Unit tests ...
* Milestone 1: \[DONE\] Supporting Documentation, Use Cases, Class Skeletons, uploaded.
* Milestone 2: Check in working Code to support the basic Zend_Service_Chargify instantiation
* Milestone 3: Check in working code to support Products functionality
* Milestone 4: Check in working code to support Coupons functionality
* Milestone 5: Check in working code to support Customers functionality
* Milestone 6: Check in working code to support Subscriptions functionality
* Milestone 7: Check in working code to support Transactions functionality
* Milestone 8: Check in working code to support Migrations functionality
* Milestone 9: Check in working code to support Charges functionality
* Milestone 10: Check in working code to support Adjustments functionality
* Milestone 11: Check in working code to support Metered Usage functionality
* Milestone 12: Check in working code to support Quantity Component Allocations functionality
* Milestone 13: Check in working code to support Refunds functionality
* Milestone 14: Check in working code to support Credits functionality
* Milestone 15: PHPUnit Test exist.
* Milestone 16. Redesign and update code based off community feedback.
{zone-data}

{zone-data:class-list}
* Zend_Magic_Exception Zend_Service_Chargify
* Zend_Magic (factory class)
* Zend_Magic_MindProbe Zend_Service_Chargify_Abstract
* Zend_Magic_MindProbe_Intent Zend_Service_Chargify_Product
* Zend_Magic_Action Zend_Service_Chargify_Coupon
* Zend_Magic_CodeGen Zend_Service_Chargify_Customer
* Zend_Service_Chargify_Subscription
* Zend_Service_Chargify_Transaction
* Zend_Service_Chargify_Migration
* Zend_Service_Chargify_Charge
* Zend_Service_Chargify_Adjustement
* Zend_Service_Chargify_MeteredUsage
* Zend_Service_Chargify_QuantityComponent
* Zend_Service_Chargify_Refund
* Zend_Service_Chargify_Credit
{zone-data}

{zone-data:use-cases}
||UC-01||
Create new customer and subscription at the same time
{code}
 <?php
// this could also be a Zend_Config object
$config = array(
  'format'   => 'json',
  'api_key'  => 'yourapikey',
  'hostname' => 'yoursite.chargify.com',
  'password' => 'x' // password is always x. might not need to send this as a config option
);
$service = new Zend_Service_Chargify($config);
Zend_Service_Chargify::setService($service); // you only need to do this one time

... (see good use cases book)
// create customer and subscription
$subscription = new Zend_Service_Chargify_Subscription();
$subscription->setProductHandle('your-product-handle')
             ->setCustomerAttributes(array(
               'first_name'   => 'First Name',
               'last_name'    => 'Last Name',
               'email'        => 'user@email.com,
               'reference'    => $uniqueUserIdFromYourApp
             ));
             
$response = $subscription->create();

// check for errors
if ($subscription->isError()) {
  $errors = $subscription->getErrors();
}
{code}

||UC-02||
Cancel a subscription
{code}
<?php
$subscription = new Zend_Service_Chargify_Subscription();
$subscription->cancel(123456);
{code}

||UC-03||
List transactions for a specific subscription. charges, refunds and payments only
{code}
<?php
$trans = new Zend_Service_Chargify_Transaction();
$trans->setPagination(1, 20)
->setKinds(array('charge', 'refund', 'payment'));
$transactions = $trans->listBySubscription(123456);
{code}
 
{zone-data}

{zone-data:skeletons}
{code}
class Zend_Magic_Exception extends Zend_Exception {}
<?php
class Zend_Service_Chargify extends Zend_Service_Abstract
{
/**
* The complete hostname; e.g. "my-app-subdomain.chargify.com",
* not just "my-app-subdomain"
*
* @var string
*/
protected $_hostname;

/**
* Your api key
*
* @var string
*/
protected $_apiKey;

/**
* Your http authentication password. Your password is always "x" but
* for now it will be sent in via $config object in constructor
*
* @var string
*/
protected $_password;

/**
* xml or json
*
* @var string
*/
protected $_format;

/**
* Original config used in constructor
*
* @var array|Zend_Config
*/
protected $_config;

/**
*
* @var Core_Service_Chargify
*/
protected static $_service;

/**
* Set up configuration options for use by Zend_Http_Client
*
* @param array|Zend_Config $config
*/
public function __construct($config) {}

/**
* Set static Zend_Service_Chargify for reuse
*
* @param Zend_Service_Chargify $service
*/
public static function setService(Zend_Service_Chargify $service) {}

/**
* Get the static Zend_Service_Chargify object
*
* @return Zend_Service_Chargify
*/
public static function getService() {}

/**
* Send the request
*
* @param string $path
* @param string $method; GET, POST, PUST, DELETE
* @param string $rawData
* @param array $params
* @return Zend_Http_Response
*/
public function request($path, $method, $rawData = NULL, $params = NULL) {}

/**
* xml or json
*
* @return string
*/
public function getFormat() {}
}
{code}

class Zend_Magic {
...
{code}
<?php
abstract class Zend_Service_Chargify_Abstract
{
protected $_params = array();

protected $_errors = array();

/**
* Generic setter of parameters to be used in the request
*
* @param string $param
* @param mixed string|array $value
* @return Zend_Service_Chargify_Abstract
*/
public function setParam($param, $value) {}

/**
* Generic getter of params
*
* @param string $paramName
* @return string|array
*/
public function getParam($paramName) {}

/**
* Get all params
*
* @return array
*/
public function getParams() {}

/**
* Assmbles xml from given array
*
* @param array $array
* @return string
*/
public function arrayToXml($array) {}

/**
* Assembles the raw data (xml or json) from the given array
*
* @param array $array
* @return string
*/
public function getRawData($array) {}

/**
* Helper to determine if there are errors with the request
*
* @return bool
*/
public function isError() {}

/**
* Get the errors
*
* @return array
*/
public function getErrors() {}

/**
* Transfoms the response body (xml or json) into an array we can more easily
* work with.
*
* @param Zend_Http_Response $response
* @return array
*/
public function getResponseArray(Zend_Http_Response $response) {}
}
{code}

{code}
<?php
/**
* Class for interacting with Customer functionality
*
* @link http://docs.chargify.com/api-customers
*/
class Zend_Service_Chargify_Customer extends Zend_Service_Chargify_Abstract
{
/**
* (Required)
*
* @param string $firstName
* @return Zend_Service_Chargify_Customer
*/
public function setFirstName($firstName) {}

/**
* (Required)
*
* @param string $lastName
* @return Zend_Service_Chargify_Customer
*/
public function setLastName($lastName) {}

/**
* (Required)
*
* @param string $email
* @return Zend_Service_Chargify_Customer
*/
public function setEmail($email) {}

/**
* (Optional) Company/Organization name
*
* @param string $organization
* @return Core_Service_Chargify_Customer
*/
public function setOrganization($organization) {}

/**
* (Optional, but encouraged) The unique identifier used within your own
* application for this customer
*
* @param string|int $reference
* @return Core_Service_Chargify_Customer
*/
public function setReference($reference) {}

/**
* Update a customer by their ID within Chargify
*
* @param int $id
* @return array
*/
public function update($id) {}
}
{code}

{code}
<?php
/**
* Class for interactive with Subcription functionality
*
* @link http://docs.chargify.com/api-subscriptions
*/
class Zend_Service_Chargify_Subscription extends Zend_Service_Chargify_Abstract
{

/**
* The API Handle of the product for which you are creating a subscription.
* Required, unless a product_id is given instead.
*
* @param string $handle
* @return Zend_Service_Chargify_Subscription
*/
public function setProductHandle($handle) {}

/**
* The ID of an existing customer within Chargify. Required, unless a
* customer_reference or a set of customer_attributes is given.
*
* @param string|int $idFromChargify
* @return Zend_Service_Chargify_Subscription
*/
public function setCustomerId($idFromChargify) {}

/**
* The reference value (provided by your app) of an existing customer within
* Chargify. Required, unless a customer_id or a set of customer_attributes
* is given.
*
* @param string|int $idFromYourApp
* @return Zend_Service_Chargify_Subscription
*/
public function setCustomerReference($idFromYourApp) {}

/**
* Possible array keys:
*
* first_name
* The first name of the customer. Required when creating a customer via
* attributes.
*
* last_name
* The last name of the customer. Required when creating a customer via
* attributes.
*
* email
* The email address of the customer. Required when creating a customer via
* attributes.
*
* organization
* The organization/company of the customer. Optional.
*
* reference
* A customer "reference", or unique identifier from your app, stored in
* Chargify. Can be used so that you may reference your customers within
* Chargify using the same unique value you use in your application. Optional.
*
* @param array $attributes
* @return Zend_Service_Chargify_Subscription
*/
public function setCustomerAttributes($attributes) {}

/**
*
* Possible array keys:
*
* first_name
* (Optional) First name on card. If omitted, the first_name from customer
* attributes will be used.
*
* last_name
* (Optional) Last name on card. If omitted, the last_name from customer
* attributes will be used.
*
* full_number
* The full credit card number (string representation, i.e.
* "5424000000000015")
*
* expiration_month
* (Optional when performing a Subscription Import via `vault_token`,
* required otherwise) The 1- or 2-digit credit card expiration month, as an
* integer or string, i.e. "5"
*
* expiration_year
* (Optional when performing a Subscription Import via `vault_token`,
* required otherwise) The 4-digit credit card expiration year, as an integer
* or string, i.e. "2012"
*
* cvv
* (Optional, may be required by your gateway settings) The 3- or 4-digit
* Card Verification Value. This value is merely passed through to the
* payment gateway.
*
* billing_address
* (Optional, may be required by your product configuration or gateway
* settings) The credit card billing street address (i.e. "123 Main St.").
* This value is merely passed through to the payment gateway.
*
* billing_city
* (Optional, may be required by your product configuration or gateway
* settings) The credit card billing address city (i.e. "Boston"). This
* value is merely passed through to the payment gateway.
*
* billing_state
* (Optional, may be required by your product configuration or gateway
* settings) The credit card billing address state (i.e. "MA"). This value is
* merely passed through to the payment gateway.
*
* billing_zip
* (Optional, may be required by your product configuration or gateway
* settings) The credit card billing address zip code (i.e. "12345"). This
* value is merely passed through to the payment gateway.
*
* billing_country
* (Optional, may be required by your product configuration or gateway
* settings) The credit card billing address country, preferably in
* [ISO 3166-1 alpha-2](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
* format (i.e. "US"). This value is merely passed through to the payment
* gateway. Some gateways require country codes in a specific format. Please
* check your gateway's documentation.
*
* vault_token
* (Optional, used only for Subscription Import) The "token" provided by your
* vault storage for an already stored payment profile
*
* customer_vault_token
* (Optional, used only for Subscription Import) (only for Authorize.Net CIM
* storage) The customerProfileId for the owner of the
* customerPaymentProfileId provided as the vault_token
*
* current_vault
* (Optional, used only for Subscription Import) The vault that stores the
* payment profile with the provided vault_token. May be authorizenet,
* trust_commerce, payment_express, beanstream, or braintree1
*
* last_four
* (Optional, used only for Subscription Import) If you have the last 4
* digits of the credit card number, you may supply them here so that we may
* create a masked card number (i.e. 'XXXX-XXXX-XXXX-1234') for display in
* the UI
*
* card_type
* (Optional, used only for Subscription Import) If you know the card type
* (i.e. Visa, MC, etc) you may supply it here so that we may display the
* card type in the UI. May be visa, master, discover, american_express,
* diners_club, jcb, switch, solo, dankort, maestro, forbrugsforeningen, orlaser
*
* @param array $attributes
* @return Zend_Service_Chargify_Subscription
*/
public function setPaymentProfileAttributes($attributes) {}

/**
* (Optional) Can be used when canceling a subscription (via the HTTP DELETE
* method) to make a note about the reason for cancellation.
*
* @param string $message
* @return Zend_Service_Chargify_Subscription
*/
public function setCancellationMessage($message) {}

/**
* (Optional, used for Subscription Import) Set this attribute to a future
* date/time to sync imported subscriptions to your existing renewal
* schedule. See the notes on "Date/Time Format" below. If you provide a
* next_billing_at timestamp that is in the future, no trial or initial
* charges will be applied when you create the subscription. In fact, no
* payment will be captured at all. The first payment will be captured,
* according to the prices defined by the product, near the time specified
* by next_billing_at. If you do not provide a value for next_billing_at,
* any trial and/or initial charges will be assessed and charged at the time
* of subscription creation. If the card cannot be successfully charged, the
* subscription will not be created.
*
* @param string $nextBilling
* @return Zend_Service_Chargify_Subscription
*/
public function setNextbillingAt($nextBilling) {}

/**
* Create a subscription with params set by setters
*
* @throws Zend_Service_Chargify_Exception
* @return array
*/
public function create() {}
}
{code}

{code}
<?php
/**
* Class for interacting with Transaction functionality
*
* @link http://docs.chargify.com/api-transactions
*/
class Zend_Service_Chargify_Transaction extends Zend_Service_Chargify_Abstract
{
/**
* An array of transaction types. Multiple values can be passed in the url,
* for example: http://example.com?kinds[]=charge&kinds[]=payment&kinds[]=credit
*
* The following is a list of available transaction types.
*
* charge
* refund
* payment
* credit
* payment_authorization
* info
* adjustment
*
* @param array $kinds
* @return Zend_Service_Chargify_Transaction
*/
public function setKinds($kinds) {}

/**
* Returns transactions with an id greater than or equal to the one specified
*
* @param int $sinceId
* @return Zend_Service_Chargify_Transaction
*/
public function setSinceId($sinceId) {}

/**
* Returns transactions with an id less than or equal to the one specified
*
* @param int $maxId
* @return Zend_Service_Chargify_Transaction
*/
public function setMaxId($maxId) {}

/**
* Returns transactions with a created_at date greater than or equal to the
* one specified
*
* @param string $sinceDate; format YYYY-MM-DD
* @return Zend_Service_Chargify_Transaction
*/
public function setSinceDate($sinceDate) {}

/**
* Returns transactions with a created_at date less than or equal to the one specified
*
* @param string $untilDate; format YYYY-MM-DD
* @return Zend_Service_Chargify_Transaction
*/
public function setUntilDate($untilDate) {}

/**
* The page number and number of results used for pagination. By default
* results are paginated 20 per page.
*
* @param int $page
* @param int $perPage
* @return Zend_Service_Chargify_Transaction
*/
public function setPagination($page, $perPage) {}

/**
* Retrieve transactions for a specific subscription
*
* @param int $subscriptionId; Chargify subscription_id
* @return array
*/
public function listBySubscription($subscriptionId) {}

/**
* Depending on what format you are using some of the associative array keys
* get nested differently. This fixes them up so you always get the same array.
*
* @param array $responseArray
* @return array
*/
protected function _normalizeResponseArray($responseArray) {}
}
{code}

{zone-data}