1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-17 20:32:41 +01:00

Update Stripe PHP API

Summary: Ref T2787. This brings us up to date.

Test Plan: `git clone`

Reviewers: btrahan, chad

Reviewed By: chad

Subscribers: epriestley

Maniphest Tasks: T2787

Differential Revision: https://secure.phabricator.com/D9916
This commit is contained in:
epriestley 2014-07-13 09:19:07 -07:00
parent 3a59cff7e6
commit 643c1c4a52
37 changed files with 6020 additions and 3282 deletions

17
externals/stripe-php/.travis.yml vendored Normal file
View file

@ -0,0 +1,17 @@
language: php
php:
- 5.2
- 5.3
- 5.4
- 5.5
- 5.6
- hhvm
before_script:
- sh -c "if [ '$TRAVIS_PHP_VERSION' = '5.2' ]; then wget http://iweb.dl.sourceforge.net/project/simpletest/simpletest/simpletest_1.1/simpletest_1.1.0.tar.gz; tar xf simpletest_1.1.0.tar.gz -C test; else composer install --dev --prefer-source; fi"
- sh -c "if [ '$TRAVIS_PHP_VERSION' = '5.6' ]; then pear install pear/PHP_CodeSniffer; phpenv rehash; fi"
script:
- sh -c "if [ '$TRAVIS_PHP_VERSION' = '5.6' ]; then phpcs --standard=zend --encoding=UTF-8 --ignore=vendor -p ./; fi"
- php test/Stripe.php

156
externals/stripe-php/CHANGELOG vendored Normal file
View file

@ -0,0 +1,156 @@
=== 1.16.0 2014-06-17
* Add metadata for refunds and disputes
=== 1.15.0 2014-05-28
* Support canceling transfers
=== 1.14.1 2014-05-21
* Support cards for recipients.
=== 1.13.1 2014-05-15
* Fix bug in account resource where `id` wasn't in the result
=== 1.13.0 2014-04-10
* Add support for certificate blacklisting
* Update ca bundle
* Drop support for HHVM (Temporarily)
=== 1.12.0 2014-04-01
* Add Stripe_RateLimitError for catching rate limit errors.
* Update to Zend coding style (thanks, @jpiasetz)
=== 1.11.0 2014-01-29
* Add support for multiple subscriptions per customer
=== 1.10.1 2013-12-02
* Add new ApplicationFee
=== 1.9.1 2013-11-08
* Fix a bug where a null nestable object causes warnings to fire.
=== 1.9.0 2013-10-16
* Add support for metadata API.
=== 1.8.4 2013-09-18
* Add support for closing disputes.
=== 1.8.3 2013-08-13
* Add new Balance and BalanceTransaction
=== 1.8.2 2013-08-12
* Add support for unsetting attributes by updating to NULL.
Setting properties to a blank string is now an error.
=== 1.8.1 2013-07-12
* Add support for multiple cards API (Stripe API version 2013-07-12: https://stripe.com/docs/upgrades#2013-07-05)
=== 1.8.0 2013-04-11
* Allow Transfers to be creatable
* Add new Recipient resource
=== 1.7.15 2013-02-21
* Add 'id' to the list of permanent object attributes
=== 1.7.14 2013-02-20
* Don't re-encode strings that are already encoded in UTF-8. If you
were previously using plan or coupon objects with UTF-8 IDs, they
may have been treated as ISO-8859-1 (Latin-1) and encoded to UTF-8 a
2nd time. You may now need to pass the IDs to utf8_encode before
passing them to Stripe_Plan::retrieve or Stripe_Coupon::retrieve.
* Ensure that all input is encoded in UTF-8 before submitting it to
Stripe's servers. (github issue #27)
=== 1.7.13 2013-02-01
* Add support for passing options when retrieving Stripe objects
e.g., Stripe_Charge::retrieve(array("id"=>"foo", "expand" => array("customer")))
Stripe_Charge::retrieve("foo") will continue to work
=== 1.7.12 2013-01-15
* Add support for setting a Stripe API version override
=== 1.7.11 2012-12-30
* Version bump to cleanup constants and such (github issue #26)
=== 1.7.10 2012-11-08
* Add support for updating charge disputes.
* Fix bug preventing retrieval of null attributes
=== 1.7.9 2012-11-08
* Fix usage under autoloaders such as the one generated by composer
(github issue #22)
=== 1.7.8 2012-10-30
* Add support for creating invoices.
* Add support for new invoice lines return format
* Add support for new list objects
=== 1.7.7 2012-09-14
* Get all of the various version numbers in the repo in sync (no other
changes)
=== 1.7.6 2012-08-31
* Add update and pay methods to Invoice resource
=== 1.7.5 2012-08-23
* Change internal function names so that Stripe_SingletonApiRequest is
E_STRICT-clean (github issue #16)
=== 1.7.4 2012-08-21
* Bugfix so that Stripe objects (e.g. Customer, Charge objects) used
in API calls are transparently converted to their object IDs
=== 1.7.3 2012-08-15
* Add new Account resource
=== 1.7.2 2012-06-26
* Make clearer that you should be including lib/Stripe.php, not
test/Stripe.php (github issue #14)
=== 1.7.1 2012-05-24
* Add missing argument to Stripe_InvalidRequestError constructor in
Stripe_ApiResource::instanceUrl. Fixes a warning when
Stripe_ApiResource::instanceUrl is called on a resource with no ID
(github issue #12)
=== 1.7.0 2012-05-17
* Support Composer and Packagist (github issue #9)
* Add new deleteDiscount method to Stripe_Customer
* Add new Transfer resource
* Switch from using HTTP Basic auth to Bearer auth. (Note: Stripe will
support Basic auth for the indefinite future, but recommends Bearer
auth when possible going forward)
* Numerous test suite improvements

View file

@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2010 Stripe
Copyright (c) 2010-2014 Stripe
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -1,13 +1,49 @@
= Installation
= Stripe PHP bindings
{<img src="https://travis-ci.org/stripe/stripe-php.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/stripe/stripe-php]
{<img src="https://poser.pugx.org/stripe/stripe-php/v/stable.svg" alt="Latest Stable Version" />}[https://packagist.org/packages/stripe/stripe-php]
{<img src="https://poser.pugx.org/stripe/stripe-php/downloads.svg" alt="Total Downloads" />}[https://packagist.org/packages/stripe/stripe-php]
{<img src="https://poser.pugx.org/stripe/stripe-php/license.svg" alt="License" />}[https://packagist.org/packages/stripe/stripe-php]
You can sign up for a Stripe account at https://stripe.com.
== Requirements
PHP 5.2 and later.
== Composer
You can install the bindings via Composer[http://getcomposer.org/]. Add this to your +composer.json+:
{
"require": {
"stripe/stripe-php": "1.*"
}
}
Then install via:
composer.phar install
To use the bindings, either user Composer's autoload[https://getcomposer.org/doc/00-intro.md#autoloading]:
require_once('vendor/autoload.php');
Or manually:
require_once('/path/to/vendor/stripe/stripe-php/lib/Stripe.php');
== Manual Installation
Obtain the latest version of the Stripe PHP bindings with:
git clone https://github.com/stripe/stripe-php
To get started, add the following to your PHP script:
To use the bindings, add the following to your PHP script:
require_once("/path/to/stripe-php/lib/Stripe.php");
== Getting Started
Simple usage looks like:
Stripe::setApiKey('d8e8fca2dc0f896fd7cb4cb0031ba249');
@ -15,6 +51,17 @@ Simple usage looks like:
$charge = Stripe_Charge::create(array('card' => $myCard, 'amount' => 2000, 'currency' => 'usd'));
echo $charge;
= Documentation
== Documentation
Please see https://stripe.com/api for up-to-date documentation.
== Tests
In order to run tests you have to install SimpleTest[http://packagist.org/packages/simpletest/simpletest] via Composer[http://getcomposer.org/] (recommended way):
composer.phar update --dev
Run test suite:
php ./test/Stripe.php

View file

@ -1 +1 @@
1.6.3
1.16.0

29
externals/stripe-php/composer.json vendored Normal file
View file

@ -0,0 +1,29 @@
{
"name": "stripe/stripe-php",
"description": "Stripe PHP Library",
"keywords": [
"stripe",
"payment processing",
"api"
],
"homepage": "https://stripe.com/",
"license": "MIT",
"authors": [
{
"name": "Stripe and contributors",
"homepage": "https://github.com/stripe/stripe-php/contributors"
}
],
"require": {
"php": ">=5.2",
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*"
},
"require-dev": {
"simpletest/simpletest": "*"
},
"autoload": {
"classmap": ["lib/Stripe/"]
}
}

View file

@ -9,34 +9,12 @@ if (!function_exists('curl_init')) {
if (!function_exists('json_decode')) {
throw new Exception('Stripe needs the JSON PHP extension.');
}
abstract class Stripe
{
public static $apiKey;
public static $apiBase = 'https://api.stripe.com/v1';
public static $verifySslCerts = true;
const VERSION = '1.6.3';
public static function getApiKey()
{
return self::$apiKey;
}
public static function setApiKey($apiKey)
{
self::$apiKey = $apiKey;
}
public static function getVerifySslCerts() {
return self::$verifySslCerts;
}
public static function setVerifySslCerts($verify) {
self::$verifySslCerts = $verify;
}
if (!function_exists('mb_detect_encoding')) {
throw new Exception('Stripe needs the Multibyte String PHP extension.');
}
// Stripe singleton
require(dirname(__FILE__) . '/Stripe/Stripe.php');
// Utilities
require(dirname(__FILE__) . '/Stripe/Util.php');
@ -49,18 +27,31 @@ require(dirname(__FILE__) . '/Stripe/ApiConnectionError.php');
require(dirname(__FILE__) . '/Stripe/AuthenticationError.php');
require(dirname(__FILE__) . '/Stripe/CardError.php');
require(dirname(__FILE__) . '/Stripe/InvalidRequestError.php');
require(dirname(__FILE__) . '/Stripe/RateLimitError.php');
// Plumbing
require(dirname(__FILE__) . '/Stripe/Object.php');
require(dirname(__FILE__) . '/Stripe/ApiRequestor.php');
require(dirname(__FILE__) . '/Stripe/ApiResource.php');
require(dirname(__FILE__) . '/Stripe/SingletonApiResource.php');
require(dirname(__FILE__) . '/Stripe/AttachedObject.php');
require(dirname(__FILE__) . '/Stripe/List.php');
// Stripe API Resources
require(dirname(__FILE__) . '/Stripe/Account.php');
require(dirname(__FILE__) . '/Stripe/Card.php');
require(dirname(__FILE__) . '/Stripe/Balance.php');
require(dirname(__FILE__) . '/Stripe/BalanceTransaction.php');
require(dirname(__FILE__) . '/Stripe/Charge.php');
require(dirname(__FILE__) . '/Stripe/Customer.php');
require(dirname(__FILE__) . '/Stripe/Invoice.php');
require(dirname(__FILE__) . '/Stripe/InvoiceItem.php');
require(dirname(__FILE__) . '/Stripe/Plan.php');
require(dirname(__FILE__) . '/Stripe/Subscription.php');
require(dirname(__FILE__) . '/Stripe/Token.php');
require(dirname(__FILE__) . '/Stripe/Coupon.php');
require(dirname(__FILE__) . '/Stripe/Event.php');
require(dirname(__FILE__) . '/Stripe/Event.php');
require(dirname(__FILE__) . '/Stripe/Transfer.php');
require(dirname(__FILE__) . '/Stripe/Recipient.php');
require(dirname(__FILE__) . '/Stripe/Refund.php');
require(dirname(__FILE__) . '/Stripe/ApplicationFee.php');

View file

@ -0,0 +1,15 @@
<?php
class Stripe_Account extends Stripe_SingletonApiResource
{
/**
* @param string|null $apiKey
*
* @return Stripe_Account
*/
public static function retrieve($apiKey=null)
{
$class = get_class();
return self::_scopedSingletonRetrieve($class, $apiKey);
}
}

View file

@ -2,31 +2,57 @@
class Stripe_ApiRequestor
{
/**
* @var string $apiKey The API key that's to be used to make requests.
*/
public $apiKey;
private static $_preFlight;
private static function blacklistedCerts()
{
return array(
'05c0b3643694470a888c6e7feb5c9e24e823dc53',
'5b7dc7fbc98d78bf76d4d4fa6f597a0c901fad5c',
);
}
public function __construct($apiKey=null)
{
$this->_apiKey = $apiKey;
}
/**
* @param string $url The path to the API endpoint.
*
* @returns string The full path.
*/
public static function apiUrl($url='')
{
$apiBase = Stripe::$apiBase;
return "$apiBase$url";
}
/**
* @param string|mixed $value A string to UTF8-encode.
*
* @returns string|mixed The UTF8-encoded string, or the object passed in if
* it wasn't a string.
*/
public static function utf8($value)
{
if (is_string($value))
if (is_string($value)
&& mb_detect_encoding($value, "UTF-8", TRUE) != "UTF-8") {
return utf8_encode($value);
else
} else {
return $value;
}
}
private static function _encodeObjects($d)
{
if ($d instanceof Stripe_ApiRequestor) {
return $d->id;
if ($d instanceof Stripe_ApiResource) {
return self::utf8($d->id);
} else if ($d === true) {
return 'true';
} else if ($d === false) {
@ -34,70 +60,142 @@ class Stripe_ApiRequestor
} else if (is_array($d)) {
$res = array();
foreach ($d as $k => $v)
$res[$k] = self::_encodeObjects($v);
$res[$k] = self::_encodeObjects($v);
return $res;
} else {
return $d;
return self::utf8($d);
}
}
public static function encode($d)
/**
* @param array $arr An map of param keys to values.
* @param string|null $prefix (It doesn't look like we ever use $prefix...)
*
* @returns string A querystring, essentially.
*/
public static function encode($arr, $prefix=null)
{
return http_build_query($d, null, '&');
if (!is_array($arr))
return $arr;
$r = array();
foreach ($arr as $k => $v) {
if (is_null($v))
continue;
if ($prefix && $k && !is_int($k))
$k = $prefix."[".$k."]";
else if ($prefix)
$k = $prefix."[]";
if (is_array($v)) {
$r[] = self::encode($v, $k, true);
} else {
$r[] = urlencode($k)."=".urlencode($v);
}
}
return implode("&", $r);
}
public function request($meth, $url, $params=null)
/**
* @param string $method
* @param string $url
* @param array|null $params
*
* @return array An array whose first element is the response and second
* element is the API key used to make the request.
*/
public function request($method, $url, $params=null)
{
if (!$params)
$params = array();
list($rbody, $rcode, $myApiKey) = $this->_requestRaw($meth, $url, $params);
list($rbody, $rcode, $myApiKey) =
$this->_requestRaw($method, $url, $params);
$resp = $this->_interpretResponse($rbody, $rcode);
return array($resp, $myApiKey);
}
/**
* @param string $rbody A JSON string.
* @param int $rcode
* @param array $resp
*
* @throws Stripe_InvalidRequestError if the error is caused by the user.
* @throws Stripe_AuthenticationError if the error is caused by a lack of
* permissions.
* @throws Stripe_CardError if the error is the error code is 402 (payment
* required)
* @throws Stripe_ApiError otherwise.
*/
public function handleApiError($rbody, $rcode, $resp)
{
if (!is_array($resp) || !isset($resp['error']))
throw new Stripe_ApiError("Invalid response object from API: $rbody (HTTP response code was $rcode)", $rcode, $rbody, $resp);
if (!is_array($resp) || !isset($resp['error'])) {
$msg = "Invalid response object from API: $rbody "
."(HTTP response code was $rcode)";
throw new Stripe_ApiError($msg, $rcode, $rbody, $resp);
}
$error = $resp['error'];
$msg = isset($error['message']) ? $error['message'] : null;
$param = isset($error['param']) ? $error['param'] : null;
$code = isset($error['code']) ? $error['code'] : null;
switch ($rcode) {
case 400:
if ($code == 'rate_limit') {
throw new Stripe_RateLimitError(
$msg, $param, $rcode, $rbody, $resp
);
}
case 404:
throw new Stripe_InvalidRequestError(isset($error['message']) ? $error['message'] : null,
isset($error['param']) ? $error['param'] : null,
$rcode, $rbody, $resp);
throw new Stripe_InvalidRequestError(
$msg, $param, $rcode, $rbody, $resp
);
case 401:
throw new Stripe_AuthenticationError(isset($error['message']) ? $error['message'] : null, $rcode, $rbody, $resp);
throw new Stripe_AuthenticationError($msg, $rcode, $rbody, $resp);
case 402:
throw new Stripe_CardError(isset($error['message']) ? $error['message'] : null,
isset($error['param']) ? $error['param'] : null,
isset($error['code']) ? $error['code'] : null,
$rcode, $rbody, $resp);
throw new Stripe_CardError($msg, $param, $code, $rcode, $rbody, $resp);
default:
throw new Stripe_ApiError(isset($error['message']) ? $error['message'] : null, $rcode, $rbody, $resp);
throw new Stripe_ApiError($msg, $rcode, $rbody, $resp);
}
}
private function _requestRaw($meth, $url, $params)
private function _requestRaw($method, $url, $params)
{
$myApiKey = $this->_apiKey;
if (!$myApiKey)
$myApiKey = Stripe::$apiKey;
if (!$myApiKey)
throw new Stripe_AuthenticationError('No API key provided. (HINT: set your API key using "Stripe::setApiKey(<API-KEY>)". You can generate API keys from the Stripe web interface. See https://stripe.com/api for details, or email support@stripe.com if you have any questions.');
if (!$myApiKey) {
$msg = 'No API key provided. (HINT: set your API key using '
. '"Stripe::setApiKey(<API-KEY>)". You can generate API keys from '
. 'the Stripe web interface. See https://stripe.com/api for '
. 'details, or email support@stripe.com if you have any questions.';
throw new Stripe_AuthenticationError($msg);
}
$absUrl = $this->apiUrl($url);
$params = self::_encodeObjects($params);
$langVersion = phpversion();
$uname = php_uname();
$ua = array('bindings_version' => Stripe::VERSION,
'lang' => 'php',
'lang_version' => $langVersion,
'publisher' => 'stripe',
'uname' => $uname);
'lang' => 'php',
'lang_version' => $langVersion,
'publisher' => 'stripe',
'uname' => $uname);
$headers = array('X-Stripe-Client-User-Agent: ' . json_encode($ua),
'User-Agent: Stripe/v1 PhpBindings/' . Stripe::VERSION);
list($rbody, $rcode) = $this->_curlRequest($meth, $absUrl, $headers, $params, $myApiKey);
'User-Agent: Stripe/v1 PhpBindings/' . Stripe::VERSION,
'Authorization: Bearer ' . $myApiKey);
if (Stripe::$apiVersion)
$headers[] = 'Stripe-Version: ' . Stripe::$apiVersion;
list($rbody, $rcode) = $this->_curlRequest(
$method,
$absUrl,
$headers,
$params
);
return array($rbody, $rcode, $myApiKey);
}
@ -106,7 +204,9 @@ class Stripe_ApiRequestor
try {
$resp = json_decode($rbody, true);
} catch (Exception $e) {
throw new Stripe_ApiError("Invalid response body from API: $rbody (HTTP response code was $rcode)", $rcode, $rbody);
$msg = "Invalid response body from API: $rbody "
. "(HTTP response code was $rcode)";
throw new Stripe_ApiError($msg, $rcode, $rbody);
}
if ($rcode < 200 || $rcode >= 300) {
@ -115,28 +215,33 @@ class Stripe_ApiRequestor
return $resp;
}
private function _curlRequest($meth, $absUrl, $headers, $params, $myApiKey)
private function _curlRequest($method, $absUrl, $headers, $params)
{
if (!self::$_preFlight) {
self::$_preFlight = $this->checkSslCert($this->apiUrl());
}
$curl = curl_init();
$meth = strtolower($meth);
$method = strtolower($method);
$opts = array();
if ($meth == 'get') {
if ($method == 'get') {
$opts[CURLOPT_HTTPGET] = 1;
if (count($params) > 0) {
$encoded = self::encode($params);
$absUrl = "$absUrl?$encoded";
$encoded = self::encode($params);
$absUrl = "$absUrl?$encoded";
}
} else if ($meth == 'post') {
} else if ($method == 'post') {
$opts[CURLOPT_POST] = 1;
$opts[CURLOPT_POSTFIELDS] = self::encode($params);
} else if ($meth == 'delete') {
} else if ($method == 'delete') {
$opts[CURLOPT_CUSTOMREQUEST] = 'DELETE';
if (count($params) > 0) {
$encoded = self::encode($params);
$absUrl = "$absUrl?$encoded";
$encoded = self::encode($params);
$absUrl = "$absUrl?$encoded";
}
} else {
throw new Stripe_ApiError("Unrecognized method $meth");
throw new Stripe_ApiError("Unrecognized method $method");
}
$absUrl = self::utf8($absUrl);
@ -146,19 +251,27 @@ class Stripe_ApiRequestor
$opts[CURLOPT_TIMEOUT] = 80;
$opts[CURLOPT_RETURNTRANSFER] = true;
$opts[CURLOPT_HTTPHEADER] = $headers;
$opts[CURLOPT_USERPWD] = $myApiKey . ':';
if (!Stripe::$verifySslCerts)
$opts[CURLOPT_SSL_VERIFYPEER] = false;
curl_setopt_array($curl, $opts);
$rbody = curl_exec($curl);
if (!defined('CURLE_SSL_CACERT_BADFILE')) {
define('CURLE_SSL_CACERT_BADFILE', 77); // constant not defined in PHP
}
$errno = curl_errno($curl);
if ($errno == CURLE_SSL_CACERT || $errno == CURLE_SSL_PEER_CERTIFICATE) {
array_push($headers, 'X-Stripe-Client-Info: {"ca":"using Stripe-supplied CA bundle"}');
if ($errno == CURLE_SSL_CACERT ||
$errno == CURLE_SSL_PEER_CERTIFICATE ||
$errno == CURLE_SSL_CACERT_BADFILE) {
array_push(
$headers,
'X-Stripe-Client-Info: {"ca":"using Stripe-supplied CA bundle"}'
);
$cert = $this->caBundle();
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_CAINFO,
dirname(__FILE__) . '/../data/ca-certificates.crt');
curl_setopt($curl, CURLOPT_CAINFO, $cert);
$rbody = curl_exec($curl);
}
@ -174,6 +287,11 @@ class Stripe_ApiRequestor
return array($rbody, $rcode);
}
/**
* @param number $errno
* @param string $message
* @throws Stripe_ApiConnectionError
*/
public function handleCurlError($errno, $message)
{
$apiBase = Stripe::$apiBase;
@ -181,17 +299,119 @@ class Stripe_ApiRequestor
case CURLE_COULDNT_CONNECT:
case CURLE_COULDNT_RESOLVE_HOST:
case CURLE_OPERATION_TIMEOUTED:
$msg = "Could not connect to Stripe ($apiBase). Please check your internet connection and try again. If this problem persists, you should check Stripe's service status at https://twitter.com/stripestatus, or let us know at support@stripe.com.";
break;
$msg = "Could not connect to Stripe ($apiBase). Please check your "
. "internet connection and try again. If this problem persists, "
. "you should check Stripe's service status at "
. "https://twitter.com/stripestatus, or";
break;
case CURLE_SSL_CACERT:
case CURLE_SSL_PEER_CERTIFICATE:
$msg = "Could not verify Stripe's SSL certificate. Please make sure that your network is not intercepting certificates. (Try going to $apiBase in your browser.) If this problem persists, let us know at support@stripe.com.";
break;
$msg = "Could not verify Stripe's SSL certificate. Please make sure "
. "that your network is not intercepting certificates. "
. "(Try going to $apiBase in your browser.) "
. "If this problem persists,";
break;
default:
$msg = "Unexpected error communicating with Stripe. If this problem persists, let us know at support@stripe.com.";
$msg = "Unexpected error communicating with Stripe. "
. "If this problem persists,";
}
$msg .= " let us know at support@stripe.com.";
$msg .= "\n\n(Network error: $message)";
$msg .= "\n\n(Network error [errno $errno]: $message)";
throw new Stripe_ApiConnectionError($msg);
}
/**
* Preflight the SSL certificate presented by the backend. This isn't 100%
* bulletproof, in that we're not actually validating the transport used to
* communicate with Stripe, merely that the first attempt to does not use a
* revoked certificate.
*
* Unfortunately the interface to OpenSSL doesn't make it easy to check the
* certificate before sending potentially sensitive data on the wire. This
* approach raises the bar for an attacker significantly.
*/
private function checkSslCert($url)
{
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
error_log(
'Warning: This version of PHP is too old to check SSL certificates '.
'correctly. Stripe cannot guarantee that the server has a '.
'certificate which is not blacklisted'
);
return true;
}
if (strpos(PHP_VERSION, 'hiphop') !== false) {
error_log(
'Warning: HHVM does not support Stripe\'s SSL certificate '.
'verification. (See http://docs.hhvm.com/manual/en/context.ssl.php) '.
'Stripe cannot guarantee that the server has a certificate which is '.
'not blacklisted'
);
return true;
}
$url = parse_url($url);
$port = isset($url["port"]) ? $url["port"] : 443;
$url = "ssl://{$url["host"]}:{$port}";
$sslContext = stream_context_create(
array('ssl' => array(
'capture_peer_cert' => true,
'verify_peer' => true,
'cafile' => $this->caBundle(),
))
);
$result = stream_socket_client(
$url, $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $sslContext
);
if ($errno !== 0) {
$apiBase = Stripe::$apiBase;
throw new Stripe_ApiConnectionError(
'Could not connect to Stripe (' . $apiBase . '). Please check your '.
'internet connection and try again. If this problem persists, '.
'you should check Stripe\'s service status at '.
'https://twitter.com/stripestatus. Reason was: '.$errstr
);
}
$params = stream_context_get_params($result);
$cert = $params['options']['ssl']['peer_certificate'];
openssl_x509_export($cert, $pemCert);
if (self::isBlackListed($pemCert)) {
throw new Stripe_ApiConnectionError(
'Invalid server certificate. You tried to connect to a server that '.
'has a revoked SSL certificate, which means we cannot securely send '.
'data to that server. Please email support@stripe.com if you need '.
'help connecting to the correct API server.'
);
}
return true;
}
/* Checks if a valid PEM encoded certificate is blacklisted
* @return boolean
*/
public static function isBlackListed($certificate)
{
$certificate = trim($certificate);
$lines = explode("\n", $certificate);
// Kludgily remove the PEM padding
array_shift($lines); array_pop($lines);
$derCert = base64_decode(implode("", $lines));
$fingerprint = sha1($derCert);
return in_array($fingerprint, self::blacklistedCerts());
}
private function caBundle()
{
return dirname(__FILE__) . '/../data/ca-certificates.crt';
}
}

View file

@ -9,54 +9,102 @@ abstract class Stripe_ApiResource extends Stripe_Object
return $instance;
}
/**
* @returns Stripe_ApiResource The refreshed resource.
*/
public function refresh()
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl();
list($response, $apiKey) = $requestor->request('get', $url);
list($response, $apiKey) = $requestor->request(
'get',
$url,
$this->_retrieveOptions
);
$this->refreshFrom($response, $apiKey);
return $this;
}
}
public static function classUrl($class)
/**
* @param string $class
*
* @returns string The name of the class, with namespacing and underscores
* stripped.
*/
public static function className($class)
{
// Useful for namespaces: Foo\Stripe_Charge
if ($postfix = strrchr($class, '\\'))
$class = substr($postfix, 1);
if (substr($class, 0, strlen('Stripe')) == 'Stripe')
if ($postfixNamespaces = strrchr($class, '\\')) {
$class = substr($postfixNamespaces, 1);
}
// Useful for underscored 'namespaces': Foo_Stripe_Charge
if ($postfixFakeNamespaces = strrchr($class, 'Stripe_')) {
$class = $postfixFakeNamespaces;
}
if (substr($class, 0, strlen('Stripe')) == 'Stripe') {
$class = substr($class, strlen('Stripe'));
}
$class = str_replace('_', '', $class);
$name = urlencode($class);
$name = strtolower($name);
return "/${name}s";
return $name;
}
/**
* @param string $class
*
* @returns string The endpoint URL for the given class.
*/
public static function classUrl($class)
{
$base = self::_scopedLsb($class, 'className', $class);
return "/v1/${base}s";
}
/**
* @returns string The full API URL for this API resource.
*/
public function instanceUrl()
{
$id = $this['id'];
$class = get_class($this);
if (!$id) {
throw new Stripe_InvalidRequestError("Could not determine which URL to request: $class instance has invalid ID: $id");
if ($id === null) {
$message = "Could not determine which URL to request: "
. "$class instance has invalid ID: $id";
throw new Stripe_InvalidRequestError($message, null);
}
$id = Stripe_ApiRequestor::utf8($id);
$base = self::classUrl($class);
$base = $this->_lsb('classUrl', $class);
$extn = urlencode($id);
return "$base/$extn";
}
private static function _validateCall($method, $params=null, $apiKey=null)
{
if ($params && !is_array($params))
throw new Stripe_Error("You must pass an array as the first argument to Stripe API method calls. (HINT: an example call to create a charge would be: \"StripeCharge::create(array('amount' => 100, 'currency' => 'usd', 'card' => array('number' => 4242424242424242, 'exp_month' => 5, 'exp_year' => 2015)))\")");
if ($apiKey && !is_string($apiKey))
throw new Stripe_Error('The second argument to Stripe API method calls is an optional per-request apiKey, which must be a string. (HINT: you can set a global apiKey by "Stripe::setApiKey(<apiKey>)")');
if ($params && !is_array($params)) {
$message = "You must pass an array as the first argument to Stripe API "
. "method calls. (HINT: an example call to create a charge "
. "would be: \"StripeCharge::create(array('amount' => 100, "
. "'currency' => 'usd', 'card' => array('number' => "
. "4242424242424242, 'exp_month' => 5, 'exp_year' => 2015)))\")";
throw new Stripe_Error($message);
}
if ($apiKey && !is_string($apiKey)) {
$message = 'The second argument to Stripe API method calls is an '
. 'optional per-request apiKey, which must be a string. '
. '(HINT: you can set a global apiKey by '
. '"Stripe::setApiKey(<apiKey>)")';
throw new Stripe_Error($message);
}
}
protected static function _scopedAll($class, $params=null, $apiKey=null)
{
self::_validateCall('all', $params, $apiKey);
$requestor = new Stripe_ApiRequestor($apiKey);
$url = self::classUrl($class);
$url = self::_scopedLsb($class, 'classUrl', $class);
list($response, $apiKey) = $requestor->request('get', $url, $params);
return Stripe_Util::convertToStripeObject($response, $apiKey);
}
@ -65,19 +113,18 @@ abstract class Stripe_ApiResource extends Stripe_Object
{
self::_validateCall('create', $params, $apiKey);
$requestor = new Stripe_ApiRequestor($apiKey);
$url = self::classUrl($class);
$url = self::_scopedLsb($class, 'classUrl', $class);
list($response, $apiKey) = $requestor->request('post', $url, $params);
return Stripe_Util::convertToStripeObject($response, $apiKey);
}
protected function _scopedSave($class)
protected function _scopedSave($class, $apiKey=null)
{
self::_validateCall('save');
if ($this->_unsavedValues) {
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$params = array();
foreach ($this->_unsavedValues->toArray() as $k)
$params[$k] = $this->$k;
$requestor = new Stripe_ApiRequestor($apiKey);
$params = $this->serializeParameters();
if (count($params) > 0) {
$url = $this->instanceUrl();
list($response, $apiKey) = $requestor->request('post', $url, $params);
$this->refreshFrom($response, $apiKey);

View file

@ -0,0 +1,53 @@
<?php
class Stripe_ApplicationFee extends Stripe_ApiResource
{
/**
* This is a special case because the application fee endpoint has an
* underscore in it. The parent `className` function strips underscores.
*
* @return string The name of the class.
*/
public static function className($class)
{
return 'application_fee';
}
/**
* @param string $id The ID of the application fee to retrieve.
* @param string|null $apiKey
*
* @return Stripe_ApplicationFee
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param string|null $params
* @param string|null $apiKey
*
* @return array An array of application fees.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedAll($class, $params, $apiKey);
}
/**
* @param string|null $params
*
* @return Stripe_ApplicationFee The refunded application fee.
*/
public function refund($params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl() . '/refund';
list($response, $apiKey) = $requestor->request('post', $url, $params);
$this->refreshFrom($response, $apiKey);
return $this;
}
}

View file

@ -0,0 +1,23 @@
<?php
// e.g. metadata on Stripe objects.
class Stripe_AttachedObject extends Stripe_Object
{
/**
* Updates this object.
*
* @param array $properties A mapping of properties to update on this object.
*/
public function replaceWith($properties)
{
$removed = array_diff(array_keys($this->_values), array_keys($properties));
// Don't unset, but rather set to null so we send up '' for deletion.
foreach ($removed as $k) {
$this->$k = null;
}
foreach ($properties as $k => $v) {
$this->$k = $v;
}
}
}

View file

@ -0,0 +1,15 @@
<?php
class Stripe_Balance extends Stripe_SingletonApiResource
{
/**
* @param string|null $apiKey
*
* @return Stripe_Balance
*/
public static function retrieve($apiKey=null)
{
$class = get_class();
return self::_scopedSingletonRetrieve($class, $apiKey);
}
}

View file

@ -0,0 +1,39 @@
<?php
class Stripe_BalanceTransaction extends Stripe_ApiResource
{
/**
* @param string $class Ignored.
*
* @return string The class URL for this resource. It needs to be special
* cased because it doesn't fit into the standard resource pattern.
*/
public static function classUrl($class)
{
return "/v1/balance/history";
}
/**
* @param string $id The ID of the balance transaction to retrieve.
* @param string|null $apiKey
*
* @return Stripe_BalanceTransaction
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_BalanceTransactions.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedAll($class, $params, $apiKey);
}
}

View file

@ -0,0 +1,66 @@
<?php
class Stripe_Card extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
}
/**
* @return string The instance URL for this resource. It needs to be special
* cased because it doesn't fit into the standard resource pattern.
*/
public function instanceUrl()
{
$id = $this['id'];
if (!$id) {
$class = get_class($this);
$msg = "Could not determine which URL to request: $class instance "
. "has invalid ID: $id";
throw new Stripe_InvalidRequestError($msg, null);
}
if (isset($this['customer'])) {
$parent = $this['customer'];
$base = self::classUrl('Stripe_Customer');
} else if (isset($this['recipient'])) {
$parent = $this['recipient'];
$base = self::classUrl('Stripe_Recipient');
} else {
return null;
}
$parent = Stripe_ApiRequestor::utf8($parent);
$id = Stripe_ApiRequestor::utf8($id);
$parentExtn = urlencode($parent);
$extn = urlencode($id);
return "$base/$parentExtn/cards/$extn";
}
/**
* @param array|null $params
*
* @return Stripe_Card The deleted card.
*/
public function delete($params=null)
{
$class = get_class();
return self::_scopedDelete($class, $params);
}
/**
* @return Stripe_Card The saved card.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
}

View file

@ -2,9 +2,11 @@
class Stripe_CardError extends Stripe_Error
{
public function __construct($message, $param, $code, $http_status=null, $http_body=null, $json_body=null)
public function __construct($message, $param, $code, $httpStatus,
$httpBody, $jsonBody
)
{
parent::__construct($message, $http_status, $http_body, $json_body);
parent::__construct($message, $httpStatus, $httpBody, $jsonBody);
$this->param = $param;
$this->code = $code;
}

View file

@ -2,30 +2,56 @@
class Stripe_Charge extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
}
/**
* @param string $id The ID of the charge to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Charge
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_Charges.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedAll($class, $params, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Charge The created charge.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedCreate($class, $params, $apiKey);
}
/**
* @return Stripe_Charge The saved charge.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
/**
* @param array|null $params
*
* @return Stripe_Charge The refunded charge.
*/
public function refund($params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
@ -35,6 +61,11 @@ class Stripe_Charge extends Stripe_ApiResource
return $this;
}
/**
* @param array|null $params
*
* @return Stripe_Charge The captured charge.
*/
public function capture($params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
@ -43,4 +74,30 @@ class Stripe_Charge extends Stripe_ApiResource
$this->refreshFrom($response, $apiKey);
return $this;
}
/**
* @param array|null $params
*
* @return array The updated dispute.
*/
public function updateDispute($params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl() . '/dispute';
list($response, $apiKey) = $requestor->request('post', $url, $params);
$this->refreshFrom(array('dispute' => $response), $apiKey, true);
return $this->dispute;
}
/**
* @return Stripe_Charge The updated charge.
*/
public function closeDispute()
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl() . '/dispute/close';
list($response, $apiKey) = $requestor->request('post', $url);
$this->refreshFrom($response, $apiKey);
return $this;
}
}

View file

@ -2,30 +2,47 @@
class Stripe_Coupon extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
}
/**
* @param string $id The ID of the coupon to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Coupon
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Coupon The created coupon.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedCreate($class, $params, $apiKey);
}
/**
* @param array|null $params
*
* @return Stripe_Coupon The deleted coupon.
*/
public function delete($params=null)
{
$class = get_class();
return self::_scopedDelete($class, $params);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_Coupons.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();

View file

@ -2,42 +2,67 @@
class Stripe_Customer extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
}
/**
* @param string $id The ID of the customer to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Customer
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_Customers.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedAll($class, $params, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Customer The created customer.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedCreate($class, $params, $apiKey);
}
/**
* @returns Stripe_Customer The saved customer.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
/**
* @param array|null $params
*
* @returns Stripe_Customer The deleted customer.
*/
public function delete($params=null)
{
$class = get_class();
return self::_scopedDelete($class, $params);
}
/**
* @param array|null $params
*
* @returns Stripe_InvoiceItem The resulting invoice item.
*/
public function addInvoiceItem($params=null)
{
if (!$params)
@ -47,6 +72,11 @@ class Stripe_Customer extends Stripe_ApiResource
return $ii;
}
/**
* @param array|null $params
*
* @returns array An array of the customer's Stripe_Invoices.
*/
public function invoices($params=null)
{
if (!$params)
@ -56,6 +86,11 @@ class Stripe_Customer extends Stripe_ApiResource
return $invoices;
}
/**
* @param array|null $params
*
* @returns array An array of the customer's Stripe_InvoiceItems.
*/
public function invoiceItems($params=null)
{
if (!$params)
@ -65,6 +100,11 @@ class Stripe_Customer extends Stripe_ApiResource
return $iis;
}
/**
* @param array|null $params
*
* @returns array An array of the customer's Stripe_Charges.
*/
public function charges($params=null)
{
if (!$params)
@ -74,6 +114,11 @@ class Stripe_Customer extends Stripe_ApiResource
return $charges;
}
/**
* @param array|null $params
*
* @returns Stripe_Subscription The updated subscription.
*/
public function updateSubscription($params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
@ -83,6 +128,11 @@ class Stripe_Customer extends Stripe_ApiResource
return $this->subscription;
}
/**
* @param array|null $params
*
* @returns Stripe_Subscription The cancelled subscription.
*/
public function cancelSubscription($params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
@ -91,4 +141,17 @@ class Stripe_Customer extends Stripe_ApiResource
$this->refreshFrom(array('subscription' => $response), $apiKey, true);
return $this->subscription;
}
/**
* @param array|null $params
*
* @returns Stripe_Customer The updated customer.
*/
public function deleteDiscount()
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl() . '/discount';
list($response, $apiKey) = $requestor->request('delete', $url);
$this->refreshFrom(array('discount' => null), $apiKey, true);
}
}

View file

@ -2,26 +2,28 @@
class Stripe_Error extends Exception
{
public function __construct($message=null, $http_status=null, $http_body=null, $json_body=null)
public function __construct($message, $httpStatus=null,
$httpBody=null, $jsonBody=null
)
{
parent::__construct($message);
$this->http_status = $http_status;
$this->http_body = $http_body;
$this->json_body = $json_body;
$this->httpStatus = $httpStatus;
$this->httpBody = $httpBody;
$this->jsonBody = $jsonBody;
}
public function getHttpStatus()
{
return $this->http_status;
return $this->httpStatus;
}
public function getHttpBody()
{
return $this->http_body;
return $this->httpBody;
}
public function getJsonBody()
{
return $this->json_body;
return $this->jsonBody;
}
}

View file

@ -2,18 +2,24 @@
class Stripe_Event extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
}
/**
* @param string $id The ID of the event to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Event
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_Events.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();

View file

@ -2,9 +2,11 @@
class Stripe_InvalidRequestError extends Stripe_Error
{
public function __construct($message, $param, $http_status=null, $http_body=null, $json_body=null)
public function __construct($message, $param, $httpStatus=null,
$httpBody=null, $jsonBody=null
)
{
parent::__construct($message, $http_status, $http_body, $json_body);
parent::__construct($message, $httpStatus, $httpBody, $jsonBody);
$this->param = $param;
}
}

View file

@ -2,24 +2,48 @@
class Stripe_Invoice extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Invoice The created invoice.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
return self::_scopedCreate($class, $params, $apiKey);
}
/**
* @param string $id The ID of the invoice to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Invoice
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_Invoices.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedAll($class, $params, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Invoice The upcoming invoice.
*/
public static function upcoming($params=null, $apiKey=null)
{
$requestor = new Stripe_ApiRequestor($apiKey);
@ -27,4 +51,25 @@ class Stripe_Invoice extends Stripe_ApiResource
list($response, $apiKey) = $requestor->request('get', $url, $params);
return Stripe_Util::convertToStripeObject($response, $apiKey);
}
/**
* @return Stripe_Invoice The saved invoice.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
/**
* @return Stripe_Invoice The paid invoice.
*/
public function pay()
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl() . '/pay';
list($response, $apiKey) = $requestor->request('post', $url);
$this->refreshFrom($response, $apiKey);
return $this;
}
}

View file

@ -2,36 +2,54 @@
class Stripe_InvoiceItem extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
}
/**
* @param string $id The ID of the invoice item to retrieve.
* @param string|null $apiKey
*
* @return Stripe_InvoiceItem
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_InvoiceItems.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedAll($class, $params, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_InvoiceItem The created invoice item.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedCreate($class, $params, $apiKey);
}
/**
* @return Stripe_InvoiceItem The saved invoice item.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
/**
* @return Stripe_InvoiceItem The deleted invoice item.
*/
public function delete($params=null)
{
$class = get_class();

View file

@ -0,0 +1,37 @@
<?php
class Stripe_List extends Stripe_Object
{
public function all($params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
list($response, $apiKey) = $requestor->request(
'get',
$this['url'],
$params
);
return Stripe_Util::convertToStripeObject($response, $apiKey);
}
public function create($params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
list($response, $apiKey) = $requestor->request(
'post', $this['url'], $params
);
return Stripe_Util::convertToStripeObject($response, $apiKey);
}
public function retrieve($id, $params=null)
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$base = $this['url'];
$id = Stripe_ApiRequestor::utf8($id);
$extn = urlencode($id);
list($response, $apiKey) = $requestor->request(
'get', "$base/$extn", $params
);
return Stripe_Util::convertToStripeObject($response, $apiKey);
}
}

View file

@ -2,17 +2,28 @@
class Stripe_Object implements ArrayAccess
{
public static $_permanentAttributes;
/**
* @var Stripe_Util_Set Attributes that should not be sent to the API because
* they're not updatable (e.g. API key, ID).
*/
public static $permanentAttributes;
/**
* @var Stripe_Util_Set Attributes that are nested but still updatable from
* the parent class's URL (e.g. metadata).
*/
public static $nestedUpdatableAttributes;
public static function init()
{
self::$_permanentAttributes = new Stripe_Util_Set(array('_apiKey'));
self::$permanentAttributes = new Stripe_Util_Set(array('_apiKey', 'id'));
self::$nestedUpdatableAttributes = new Stripe_Util_Set(array('metadata'));
}
protected $_apiKey;
protected $_values;
protected $_unsavedValues;
protected $_transientValues;
protected $_retrieveOptions;
public function __construct($id=null, $apiKey=null)
{
@ -20,16 +31,41 @@ class Stripe_Object implements ArrayAccess
$this->_values = array();
$this->_unsavedValues = new Stripe_Util_Set();
$this->_transientValues = new Stripe_Util_Set();
if ($id)
$this->_retrieveOptions = array();
if (is_array($id)) {
foreach ($id as $key => $value) {
if ($key != 'id') {
$this->_retrieveOptions[$key] = $value;
}
}
$id = $id['id'];
}
if ($id !== null) {
$this->id = $id;
}
}
// Standard accessor magic methods
public function __set($k, $v)
{
// TODO: may want to clear from $_transientValues. (Won't be user-visible.)
$this->_values[$k] = $v;
if (!self::$_permanentAttributes->includes($k))
if ($v === "") {
throw new InvalidArgumentException(
'You cannot set \''.$k.'\'to an empty string. '
.'We interpret empty strings as NULL in requests. '
.'You may set obj->'.$k.' = NULL to delete the property'
);
}
if (self::$nestedUpdatableAttributes->includes($k)
&& isset($this->$k) && is_array($v)) {
$this->$k->replaceWith($v);
} else {
// TODO: may want to clear from $_transientValues (Won't be user-visible).
$this->_values[$k] = $v;
}
if (!self::$permanentAttributes->includes($k))
$this->_unsavedValues->add($k);
}
public function __isset($k)
@ -44,12 +80,18 @@ class Stripe_Object implements ArrayAccess
}
public function __get($k)
{
if (isset($this->_values[$k])) {
if (array_key_exists($k, $this->_values)) {
return $this->_values[$k];
} else if ($this->_transientValues->includes($k)) {
$class = get_class($this);
$attrs = join(', ', array_keys($this->_values));
error_log("Stripe Notice: Undefined property of $class instance: $k. HINT: The $k attribute was set in the past, however. It was then wiped when refreshing the object with the result returned by Stripe's API, probably as a result of a save(). The attributes currently available on this object are: $attrs");
$message = "Stripe Notice: Undefined property of $class instance: $k. "
. "HINT: The $k attribute was set in the past, however. "
. "It was then wiped when refreshing the object "
. "with the result returned by Stripe's API, "
. "probably as a result of a save(). The attributes currently "
. "available on this object are: $attrs";
error_log($message);
return null;
} else {
$class = get_class($this);
@ -63,20 +105,35 @@ class Stripe_Object implements ArrayAccess
{
$this->$k = $v;
}
public function offsetExists($k)
{
return isset($this->$k);
return array_key_exists($k, $this->_values);
}
public function offsetUnset($k)
{
unset($this->$k);
}
public function offsetGet($k)
{
return isset($this->_values[$k]) ? $this->_values[$k] : null;
return array_key_exists($k, $this->_values) ? $this->_values[$k] : null;
}
// This unfortunately needs to be public to be used in Util.php
public function keys()
{
return array_keys($this->_values);
}
/**
* This unfortunately needs to be public to be used in Util.php
*
* @param string $class
* @param array $values
* @param string|null $apiKey
*
* @return Stripe_Object The object constructed from the given values.
*/
public static function scopedConstructFrom($class, $values, $apiKey=null)
{
$obj = new $class(isset($values['id']) ? $values['id'] : null, $apiKey);
@ -84,44 +141,108 @@ class Stripe_Object implements ArrayAccess
return $obj;
}
/**
* @param array $values
* @param string|null $apiKey
*
* @return Stripe_Object The object of the same class as $this constructed
* from the given values.
*/
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
return self::scopedConstructFrom(__CLASS__, $values, $apiKey);
}
/**
* Refreshes this object using the provided values.
*
* @param array $values
* @param string $apiKey
* @param boolean $partial Defaults to false.
*/
public function refreshFrom($values, $apiKey, $partial=false)
{
$this->_apiKey = $apiKey;
// Wipe old state before setting new. This is useful for e.g. updating a
// customer, where there is no persistent card parameter. Mark those values
// which don't persist as transient
if ($partial)
if ($partial) {
$removed = new Stripe_Util_Set();
else
} else {
$removed = array_diff(array_keys($this->_values), array_keys($values));
}
foreach ($removed as $k) {
if (self::$_permanentAttributes->includes($k))
if (self::$permanentAttributes->includes($k))
continue;
unset($this->$k);
}
foreach ($values as $k => $v) {
if (self::$_permanentAttributes->includes($k))
if (self::$permanentAttributes->includes($k) && isset($this[$k]))
continue;
$this->_values[$k] = Stripe_Util::convertToStripeObject($v, $apiKey);
if (self::$nestedUpdatableAttributes->includes($k) && is_array($v)) {
$this->_values[$k] = Stripe_Object::scopedConstructFrom(
'Stripe_AttachedObject', $v, $apiKey
);
} else {
$this->_values[$k] = Stripe_Util::convertToStripeObject($v, $apiKey);
}
$this->_transientValues->discard($k);
$this->_unsavedValues->discard($k);
}
}
/**
* @return array A recursive mapping of attributes to values for this object,
* including the proper value for deleted attributes.
*/
public function serializeParameters()
{
$params = array();
if ($this->_unsavedValues) {
foreach ($this->_unsavedValues->toArray() as $k) {
$v = $this->$k;
if ($v === NULL) {
$v = '';
}
$params[$k] = $v;
}
}
// Get nested updates.
foreach (self::$nestedUpdatableAttributes->toArray() as $property) {
if (isset($this->$property)
&& $this->$property instanceOf Stripe_Object) {
$params[$property] = $this->$property->serializeParameters();
}
}
return $params;
}
// Pretend to have late static bindings, even in PHP 5.2
protected function _lsb($method)
{
$class = get_class($this);
$args = array_slice(func_get_args(), 1);
return call_user_func_array(array($class, $method), $args);
}
protected static function _scopedLsb($class, $method)
{
$args = array_slice(func_get_args(), 2);
return call_user_func_array(array($class, $method), $args);
}
public function __toJSON()
{
if (defined('JSON_PRETTY_PRINT'))
if (defined('JSON_PRETTY_PRINT')) {
return json_encode($this->__toArray(true), JSON_PRETTY_PRINT);
else
} else {
return json_encode($this->__toArray(true));
}
}
public function __toString()
@ -131,10 +252,11 @@ class Stripe_Object implements ArrayAccess
public function __toArray($recursive=false)
{
if ($recursive)
if ($recursive) {
return Stripe_Util::convertStripeObjectToArray($this->_values);
else
} else {
return $this->_values;
}
}
}

View file

@ -2,36 +2,56 @@
class Stripe_Plan extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
}
/**
* @param string $id The ID of the plan to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Plan
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Plan The created plan.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedCreate($class, $params, $apiKey);
}
/**
* @param array|null $params
*
* @return Stripe_Plan The deleted plan.
*/
public function delete($params=null)
{
$class = get_class();
return self::_scopedDelete($class, $params);
}
/**
* @return Stripe_Plan The saved plan.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_Plans.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();

View file

@ -0,0 +1,11 @@
<?php
class Stripe_RateLimitError extends Stripe_InvalidRequestError
{
public function __construct($message, $param, $httpStatus=null,
$httpBody=null, $jsonBody=null
)
{
parent::__construct($message, $httpStatus, $httpBody, $jsonBody);
}
}

View file

@ -0,0 +1,75 @@
<?php
class Stripe_Recipient extends Stripe_ApiResource
{
/**
* @param string $id The ID of the recipient to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Recipient
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_Recipients.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedAll($class, $params, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Recipient The created recipient.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedCreate($class, $params, $apiKey);
}
/**
* @return Stripe_Recipient The saved recipient.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
/**
* @param array|null $params
*
* @return Stripe_Recipient The deleted recipient.
*/
public function delete($params=null)
{
$class = get_class();
return self::_scopedDelete($class, $params);
}
/**
* @param array|null $params
*
* @return array An array of the recipient's Stripe_Transfers.
*/
public function transfers($params=null)
{
if (!$params)
$params = array();
$params['recipient'] = $this->id;
$transfers = Stripe_Transfer::all($params, $this->_apiKey);
return $transfers;
}
}

View file

@ -0,0 +1,36 @@
<?php
class Stripe_Refund extends Stripe_ApiResource
{
/**
* @return string The API URL for this Stripe refund.
*/
public function instanceUrl()
{
$id = $this['id'];
$charge = $this['charge'];
if (!$id) {
throw new Stripe_InvalidRequestError(
"Could not determine which URL to request: " .
"class instance has invalid ID: $id",
null
);
}
$id = Stripe_ApiRequestor::utf8($id);
$charge = Stripe_ApiRequestor::utf8($charge);
$base = self::classUrl('Stripe_Charge');
$chargeExtn = urlencode($charge);
$extn = urlencode($id);
return "$base/$chargeExtn/refunds/$extn";
}
/**
* @return Stripe_Refund The saved refund.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
}

View file

@ -0,0 +1,31 @@
<?php
abstract class Stripe_SingletonApiResource extends Stripe_ApiResource
{
protected static function _scopedSingletonRetrieve($class, $apiKey=null)
{
$instance = new $class(null, $apiKey);
$instance->refresh();
return $instance;
}
/**
* @param Stripe_SingletonApiResource $class
* @return string The endpoint associated with this singleton class.
*/
public static function classUrl($class)
{
$base = self::className($class);
return "/v1/${base}";
}
/**
* @return string The endpoint associated with this singleton API resource.
*/
public function instanceUrl()
{
$class = get_class($this);
$base = self::classUrl($class);
return "$base";
}
}

View file

@ -0,0 +1,73 @@
<?php
abstract class Stripe
{
/**
* @var string The Stripe API key to be used for requests.
*/
public static $apiKey;
/**
* @var string The base URL for the Stripe API.
*/
public static $apiBase = 'https://api.stripe.com';
/**
* @var string|null The version of the Stripe API to use for requests.
*/
public static $apiVersion = null;
/**
* @var boolean Defaults to true.
*/
public static $verifySslCerts = true;
const VERSION = '1.16.0';
/**
* @return string The API key used for requests.
*/
public static function getApiKey()
{
return self::$apiKey;
}
/**
* Sets the API key to be used for requests.
*
* @param string $apiKey
*/
public static function setApiKey($apiKey)
{
self::$apiKey = $apiKey;
}
/**
* @return string The API version used for requests. null if we're using the
* latest version.
*/
public static function getApiVersion()
{
return self::$apiVersion;
}
/**
* @param string $apiVersion The API version to use for requests.
*/
public static function setApiVersion($apiVersion)
{
self::$apiVersion = $apiVersion;
}
/**
* @return boolean
*/
public static function getVerifySslCerts()
{
return self::$verifySslCerts;
}
/**
* @param boolean $verify
*/
public static function setVerifySslCerts($verify)
{
self::$verifySslCerts = $verify;
}
}

View file

@ -0,0 +1,57 @@
<?php
class Stripe_Subscription extends Stripe_ApiResource
{
/**
* @return string The API URL for this Stripe subscription.
*/
public function instanceUrl()
{
$id = $this['id'];
$customer = $this['customer'];
if (!$id) {
throw new Stripe_InvalidRequestError(
"Could not determine which URL to request: " .
"class instance has invalid ID: $id",
null
);
}
$id = Stripe_ApiRequestor::utf8($id);
$customer = Stripe_ApiRequestor::utf8($customer);
$base = self::classUrl('Stripe_Customer');
$customerExtn = urlencode($customer);
$extn = urlencode($id);
return "$base/$customerExtn/subscriptions/$extn";
}
/**
* @param array|null $params
* @return Stripe_Subscription The deleted subscription.
*/
public function cancel($params=null)
{
$class = get_class();
return self::_scopedDelete($class, $params);
}
/**
* @return Stripe_Subscription The saved subscription.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
/**
* @return Stripe_Subscription The updated subscription.
*/
public function deleteDiscount()
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl() . '/discount';
list($response, $apiKey) = $requestor->request('delete', $url);
$this->refreshFrom(array('discount' => null), $apiKey, true);
}
}

View file

@ -2,18 +2,24 @@
class Stripe_Token extends Stripe_ApiResource
{
public static function constructFrom($values, $apiKey=null)
{
$class = get_class();
return self::scopedConstructFrom($class, $values, $apiKey);
}
/**
* @param string $id The ID of the token to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Token
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Coupon The created token.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();

View file

@ -0,0 +1,62 @@
<?php
class Stripe_Transfer extends Stripe_ApiResource
{
/**
* @param string $id The ID of the transfer to retrieve.
* @param string|null $apiKey
*
* @return Stripe_Transfer
*/
public static function retrieve($id, $apiKey=null)
{
$class = get_class();
return self::_scopedRetrieve($class, $id, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return array An array of Stripe_Transfers.
*/
public static function all($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedAll($class, $params, $apiKey);
}
/**
* @param array|null $params
* @param string|null $apiKey
*
* @return Stripe_Transfer The created transfer.
*/
public static function create($params=null, $apiKey=null)
{
$class = get_class();
return self::_scopedCreate($class, $params, $apiKey);
}
/**
* @return Stripe_Transfer The canceled transfer.
*/
public function cancel()
{
$requestor = new Stripe_ApiRequestor($this->_apiKey);
$url = $this->instanceUrl() . '/cancel';
list($response, $apiKey) = $requestor->request('post', $url);
$this->refreshFrom($response, $apiKey);
return $this;
}
/**
* @return Stripe_Transfer The saved transfer.
*/
public function save()
{
$class = get_class();
return self::_scopedSave($class);
}
}

View file

@ -2,11 +2,18 @@
abstract class Stripe_Util
{
/**
* Whether the provided array (or other) is a list rather than a dictionary.
*
* @param array|mixed $array
* @return boolean True if the given object is a list.
*/
public static function isList($array)
{
if (!is_array($array))
return false;
// TODO: this isn't actually correct in general, but it's correct given Stripe's responses
// TODO: generally incorrect, but it's correct given Stripe's response
foreach (array_keys($array) as $k) {
if (!is_numeric($k))
return false;
@ -14,43 +21,67 @@ abstract class Stripe_Util
return true;
}
/**
* Recursively converts the PHP Stripe object to an array.
*
* @param array $values The PHP Stripe object to convert.
* @return array
*/
public static function convertStripeObjectToArray($values)
{
$results = array();
foreach ($values as $k => $v) {
// FIXME: this is an encapsulation violation
if (Stripe_Object::$_permanentAttributes->includes($k)) {
if ($k[0] == '_') {
continue;
}
if ($v instanceof Stripe_Object) {
$results[$k] = $v->__toArray(true);
}
else if (is_array($v)) {
} else if (is_array($v)) {
$results[$k] = self::convertStripeObjectToArray($v);
}
else {
} else {
$results[$k] = $v;
}
}
return $results;
}
/**
* Converts a response from the Stripe API to the corresponding PHP object.
*
* @param array $resp The response from the Stripe API.
* @param string $apiKey
* @return Stripe_Object|array
*/
public static function convertToStripeObject($resp, $apiKey)
{
$types = array('charge' => 'Stripe_Charge',
'customer' => 'Stripe_Customer',
'invoice' => 'Stripe_Invoice',
'invoiceitem' => 'Stripe_InvoiceItem', 'event' => 'Stripe_Event');
$types = array(
'card' => 'Stripe_Card',
'charge' => 'Stripe_Charge',
'customer' => 'Stripe_Customer',
'list' => 'Stripe_List',
'invoice' => 'Stripe_Invoice',
'invoiceitem' => 'Stripe_InvoiceItem',
'event' => 'Stripe_Event',
'transfer' => 'Stripe_Transfer',
'plan' => 'Stripe_Plan',
'recipient' => 'Stripe_Recipient',
'refund' => 'Stripe_Refund',
'subscription' => 'Stripe_Subscription'
);
if (self::isList($resp)) {
$mapped = array();
foreach ($resp as $i)
array_push($mapped, self::convertToStripeObject($i, $apiKey));
return $mapped;
} else if (is_array($resp)) {
if (isset($resp['object']) && is_string($resp['object']) && isset($types[$resp['object']]))
if (isset($resp['object'])
&& is_string($resp['object'])
&& isset($types[$resp['object']])) {
$class = $types[$resp['object']];
else
} else {
$class = 'Stripe_Object';
}
return Stripe_Object::scopedConstructFrom($class, $resp, $apiKey);
} else {
return $resp;

File diff suppressed because it is too large Load diff