This commit is contained in:
Devin Smith 2015-04-01 15:08:29 -07:00
parent 389b08c572
commit 1f392444fc
79 changed files with 2914 additions and 140 deletions

View File

@ -138,12 +138,12 @@
</text>
<stripe>
<dev>
<secret>***REMOVED***</secret>
<public>***REMOVED***</public>
<secret>_KEY_</secret>
<public>_KEY_</public>
</dev>
<live>
<secret>***REMOVED***</secret>
<public>***REMOVED***</public>
<secret>_KEY_</secret>
<public>_KEY_</public>
</live>
</stripe>
<balanced>

View File

@ -100,6 +100,7 @@ spl_autoload_register(function ($className) {
\Buzz\Bootstrap::init();
\Github\Bootstrap::init();
\Mailgun\Bootstrap::init();
\Stripe\Bootstrap::init();
$configFile = $GLOBALS['config']['dirs']['config'].'config.demo.xml';
if (file_exists($GLOBALS['config']['dirs']['config'].'config.xml')) {

View File

@ -157,9 +157,6 @@ class Crunchbutton_App extends Cana_App {
->config($config)
->postInit($params);
require_once c::config()->dirs->library . '/Cana/Stripe.php';
Stripe::setApiKey(c::config()->stripe->dev->secret);
switch ($_SERVER['SERVER_NAME']) {
case 'spicywithdelivery.com':
case 'beta.spicywithdelivery.com':
@ -462,6 +459,16 @@ class Crunchbutton_App extends Cana_App {
}
return $this->_balanced;
}
public function stripe() {
if (!$this->_stripe) {
\Stripe\Stripe::setApiKey(c::config()->stripe->{c::getEnv()}->secret);;
$this->_stripe = true;
}
return $this->_stripe;
}
public function lob($d = true) {
if (!$this->_lob) {

View File

@ -20,7 +20,8 @@ class Crunchbutton_Charge_Balanced extends Cana_Model {
$c = $this->_card->debits->create([
'amount' => $params['amount'] * 100,
'appears_on_statement_as' => 'Crunchbutton',
'description' => $params['restaurant']->name
'description' => $params['restaurant']->name,
'statement_descriptor' => $params['restaurant']->statementName()
]);
} catch (Exception $e) {

View File

@ -2,98 +2,103 @@
class Crunchbutton_Charge_Stripe extends Crunchbutton_Charge {
public function __construct($params = []) {
$this->_customer = $params['customer_id'];
$this->_card = $params['card_id'];
}
public function charge($params = []) {
$env = c::getEnv();
Stripe::setApiKey(c::config()->stripe->{$env}->secret);
c::stripe();
$success = false;
$reason = false;
$user = $params[ 'user' ];
// Start with no customer id
$customer_id = false;
// The user changed its card or it is a new one
if( $params['card']['id'] ){
// The first thing we need to do is check customer
$token = $params['card']['uri'];
// lets see if the customer exists
if ( !$user || !$user->payment_type()->stripe_id ) {
// if there is no user, create one
try {
$customer = Stripe_Customer::create( array(
'description' => "Crunchbutton",
'card' => $token
) );
} catch ( Exception $e ) {
print_r( $e );
die('creating customer error: 1');
}
} elseif ( $user && $user->payment_type()->stripe_id ) {
// if there is already a user, update it
try {
$customer = Stripe_Customer::retrieve( $user->payment_type()->stripe_id );
$customer->card = $token;
$customer->save();
} catch ( Exception $e ) {
print_r( $e );
die('creating customer error: 2');
}
}
$customer_id = $customer->id;
}
// If we don't have a card token it means the user is already a customer
else if( $user->payment_type()->stripe_id ) {
$customer_id = $user->payment_type()->stripe_id;
}
// yay, we have a valid customer
if( $customer_id ){
// Now we have to charge it
if ($params['card']) {
// create a customer if it doesnt exist
if (!$this->_customer) {
try {
$charge = Stripe_Charge::create([
'amount' => $params['amount'] * 100,
'currency' => 'usd',
'customer' => $customer_id,
'description' => $params['restaurant']->name,
] );
}
// Shit happens
catch(Stripe_CardError $e) {
Log::debug( [ 'card error' => 'card declined', 'Exception' => $e->getJsonBody(), 'type' => 'stripe error' ]);
$errors[] = 'Your card was declined. Please try again!';
} catch (Stripe_InvalidRequestError $e) {
Log::debug( [ 'card error' => 'invalid request', 'Exception' => $e->getJsonBody(), 'type' => 'stripe error' ]);
$errors[] = 'Please update your credit card information.';
} catch (Stripe_AuthenticationError $e) {
Log::debug( [ 'card error' => 'auth error', 'Exception' => $e->getJsonBody(), 'type' => 'stripe error' ]);
$errors[] = 'Please update your credit card information.';
} catch (Stripe_ApiConnectionError $e) {
Log::debug( [ 'card error' => 'api connection', 'Exception' => $e->getJsonBody(), 'type' => 'stripe error' ]);
$errors[] = 'Please update your credit card information.';
} catch (Stripe_Error $e) {
Log::debug( [ 'card error' => 'api connection', 'Exception' => $e->getJsonBody(), 'type' => 'stripe error' ]);
$errors[] = 'Please update your credit card information.';
} catch (Exception $e) {
$errors[] = 'Not enough card information.';
}
$customer = \Stripe\Customer::create([
'description' => $params['name'],
'email' => $params['email'],
'source' => $params['card']['uri']
]);
if ( $charge->paid && !$charge->refunded ) {
$success = true;
$txn = $charge->id;
}
}
if (!$success && !$errors) {
$errors[] = 'Not enough card information.';
} catch ( Exception $e ) {
$errors[] = 'Could not create customer with processor.';
}
$this->_customer = $customer->id;
// there is already a customer
} else {
try {
$customer = \Stripe\Customer::retrieve($this->_customer);
$customer->card = $params['card']['token'];
$customer->save();
} catch ( Exception $e ) {
$errors[] = 'Could not retrieve or save customer to processor.';
}
}
$this->_card = $params['card']['id'];
}
return [ 'status' => $success, 'txn' => $txn, 'errors' => $errors, 'customer' => $customer ];
// Now we have to charge it
try {
$charge = \Stripe\Charge::create([
'amount' => $params['amount'] * 100,
'currency' => 'usd',
'customer' => $this->_customer,
//'source' => $this->_card,
'description' => $params['restaurant']->name,
'capture' => c::config()->site->config('processor_payments_capture') ? true : false,
'statement_descriptor' => $params['restaurant']->statementName()
]);
} catch(\Stripe\Stripe_CardError $e) {
Log::debug( [ 'card error' => 'card declined', 'Exception' => $e->getJsonBody(), 'type' => 'stripe error' ]);
$errors[] = 'Your card was declined. Please try again!';
} catch (\Stripe\Stripe_InvalidRequestError $e) {
Log::debug( [ 'card error' => 'invalid request', 'Exception' => $e->getJsonBody(), 'type' => 'stripe error' ]);
$errors[] = 'Please update your credit card information.';
} catch (\Stripe\Stripe_AuthenticationError $e) {
Log::debug( [ 'card error' => 'auth error', 'Exception' => $e->getJsonBody(), 'type' => 'stripe error' ]);
$errors[] = 'Please update your credit card information.';
} catch (\Stripe\Stripe_ApiConnectionError $e) {
Log::debug( [ 'card error' => 'api connection', 'Exception' => $e->getJsonBody(), 'type' => 'stripe error' ]);
$errors[] = 'Please update your credit card information.';
} catch (\Stripe\Stripe_Error $e) {
Log::debug( [ 'card error' => 'api connection', 'Exception' => $e->getJsonBody(), 'type' => 'stripe error' ]);
$errors[] = 'Please update your credit card information.';
} catch (Exception $e) {
//tripe\Error\InvalidRequest
//Stripe\Error\Card
print_r($e);
$errors[] = 'Error processing credit card.';
}
if ($charge && $charge->paid && !$charge->refunded) {
$success = true;
$txn = $charge->id;
}
if (!$success && !$errors) {
$errors[] = 'Completly vague payment error. Contact support and complain. We love complaints.'."\n\n".'angrycustomers@_DOMAIN_';
}
return [
'status' => $success,
'txn' => $txn,
'errors' => $errors,
'customer' => $this->_customer,
'card' => $this->_card
];
}
}

View File

@ -564,11 +564,9 @@ class Crunchbutton_Order extends Crunchbutton_Order_Trackchange {
switch (Crunchbutton_User_Payment_Type::processor()) {
case 'stripe':
$payment_type->stripe_id = $this->_customer->id;
$payment_type->stripe_id = $this->_paymentType;
break;
case 'balanced':
default:
$payment_type->balanced_id = $this->_paymentType->id;
break;
}
@ -949,32 +947,34 @@ class Crunchbutton_Order extends Crunchbutton_Order_Trackchange {
case 'card':
$user = c::user()->id_user ? c::user() : null;
$processorId = c::config()->site->config('processor_payments') == 'balanced' ? 'balanced_id' : 'stripe_id';
if( $user ){
if ($user) {
$paymentType = $user->payment_type();
}
if (!$this->_card['id'] && !$paymentType->id_user_payment_type && $user->balanced_id) {
// user only has a balanced customer id, not a payment. copy payment type over
$paymentType = (new User_Payment_Type([
'id_user' => $user->id_user,
'active' => 1,
// 'stripe_id' => $user->stripe_id,
'balanced_id' => $user->balanced_id,
'card' => $user->card,
'card_exp_month' => $user->card_exp_month,
'card_exp_year' => $user->card_exp_year,
'date' => date('Y-m-d H:i:s')
]))->save();
// #5243 - if using stripe, get the stripe id from balanced, and update the paymenttype
if (c::config()->site->config('processor_payments') == 'stripe' && $paymentType->balanced_id && !$paymentType->stripe_id) {
$balancedCard = Crunchbutton_Balanced_Card::byId($paymentType->balanced_id);
print_r($balancedCard);
exit;
}
// use a stored users card and the apporiate payment type
if (!$this->_card['id'] && $paymentType->id_user_payment_type) {
// use a stored users card and the apporiate payment type
if ($paymentType->balanced_id) {
if (c::config()->site->config('processor_payments') == 'stripe' && $paymentType->stripe_id) {
$charge = new Charge_Stripe([
'card_id' => $paymentType->stripe_id,
'customer_id' => $user->stripe_id
]);
} elseif (c::config()->site->config('processor_payments') == 'balanced' && $paymentType->balanced_id) {
if (substr($paymentType->balanced_id,0,2) != 'CC') {
// we have stored the customer and not the payment type. need to fix that
// @todo: i dont really understand wtf this is for - devin
$cards = Crunchbutton_Balanced_Account::byId($paymentType->balanced_id)->cards;
if (get_class($cards) == 'RESTful\Collection') {
foreach ($cards as $card) {
@ -996,23 +996,19 @@ class Crunchbutton_Order extends Crunchbutton_Order_Trackchange {
$charge = new Charge_Balanced([
'card_id' => $paymentType->balanced_id
]);
} elseif ($paymentType->stripe_id) {
$charge = new Charge_Stripe([
'stripe_id' => $paymentType->stripe_id
]);
} else {
die('processor mismatch');
}
}
// create the objects with no params
if (!$charge) {
switch (Crunchbutton_User_Payment_Type::processor()) {
case 'balanced':
$charge = new Charge_Balanced();
break;
case 'stripe':
$charge = new Charge_Stripe([
'stripe_id' => $user->stripe_id
]);
$charge = new Charge_Stripe();
break;
}
}
@ -1029,6 +1025,7 @@ class Crunchbutton_Order extends Crunchbutton_Order_Trackchange {
'card' => $this->_card,
'name' => $this->name,
'address' => $this->address,
'email' => $user->email,
'phone' => $this->phone,
'user' => $user,
'restaurant' => $this->restaurant()

View File

@ -154,8 +154,17 @@ class Crunchbutton_Restaurant extends Cana_Table_Trackchange {
return $phone;
}
public function shortName() {
return $this->short_name ? $this->short_name : $this->name;
// name that appears on credit card statement
public function statementName() {
if (!isset($this->_statementName)) {
$name = $this->short_name ? $this->short_name : $this->name;
$name = preg_replace('/[^a-z ]/i','',$name);
if (strlen($name) > 22) {
$name = str_replace(' ', '', $name);
}
$this->_statementName = strtoupper(substr($name, 0, 22));
}
return $this->_statementName;
}
public function _hasOption($option, $options) {

View File

@ -3,18 +3,27 @@
class Crunchbutton_User_Payment_Type extends Cana_Table {
public function processor() {
return c::config()->processor;
return c::config()->site->config('processor_payments')->value;
}
public function getUserPaymentType( $id_user = null ){
$id_user = ( $id_user ) ? $id_user : c::user()->id_user;
if( $id_user ){
$where = ' AND ' . Crunchbutton_User_Payment_Type::processor() . '_id IS NOT NULL';
$payment = Crunchbutton_User_Payment_Type::q( 'SELECT * FROM user_payment_type WHERE id_user = ? AND active = true ' . $where . ' ORDER BY id_user_payment_type DESC LIMIT 1', [$id_user]);
if( $payment->id_user_payment_type ){
public function getUserPaymentType($id_user = null) {
$id_user = $id_user ? $id_user : c::user()->id_user;
if ($id_user) {
$payment = Crunchbutton_User_Payment_Type::q('
SELECT * FROM user_payment_type
WHERE
id_user = ?
AND active = true
AND ' . Crunchbutton_User_Payment_Type::processor() . '_id IS NOT NULL
ORDER BY id_user_payment_type DESC LIMIT 1
', [$id_user]);
if ($payment->id_user_payment_type) {
return $payment;
}
}
return false;
}

View File

@ -0,0 +1,62 @@
<?php
namespace Stripe;
class Account extends ApiResource
{
public function instanceUrl()
{
if ($this['id'] === null) {
return '/v1/account';
} else {
return parent::instanceUrl();
}
}
/**
* @param string|null $id
* @param array|string|null $opts
*
* @return Account
*/
public static function retrieve($id = null, $opts = null)
{
if (!$opts && is_string($id) && substr($id, 0, 3) === 'sk_') {
$opts = $id;
$id = null;
}
return self::_retrieve($id, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Account
*/
public static function create($params = null, $opts = null)
{
return self::_create($params, $opts);
}
/**
* @param array|string|null $opts
*
* @return Account
*/
public function save($opts = null)
{
return $this->_save();
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Account[]
*/
public static function all($params = null, $opts = null)
{
return self::_all($params, $opts);
}
}

View File

@ -0,0 +1,471 @@
<?php
namespace Stripe;
class ApiRequestor
{
private $_apiKey;
private $_apiBase;
private static $_preFlight = array();
private static $_blacklistedCerts = array(
'05c0b3643694470a888c6e7feb5c9e24e823dc53',
'5b7dc7fbc98d78bf76d4d4fa6f597a0c901fad5c',
);
public function __construct($apiKey = null, $apiBase = null)
{
$this->_apiKey = $apiKey;
if (!$apiBase) {
$apiBase = Stripe::$apiBase;
}
$this->_apiBase = $apiBase;
}
/**
* @param string|mixed $value A string to UTF8-encode.
*
* @return 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) && mb_detect_encoding($value, "UTF-8", true) != "UTF-8") {
return utf8_encode($value);
} else {
return $value;
}
}
private static function _encodeObjects($d)
{
if ($d instanceof ApiResource) {
return self::utf8($d->id);
} elseif ($d === true) {
return 'true';
} elseif ($d === false) {
return 'false';
} elseif (is_array($d)) {
$res = array();
foreach ($d as $k => $v) {
$res[$k] = self::_encodeObjects($v);
}
return $res;
} else {
return self::utf8($d);
}
}
/**
* @param array $arr An map of param keys to values.
* @param string|null $prefix (It doesn't look like we ever use $prefix...)
*
* @return string A querystring, essentially.
*/
public static function encode($arr, $prefix = 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."]";
} elseif ($prefix) {
$k = $prefix."[]";
}
if (is_array($v)) {
$r[] = self::encode($v, $k, true);
} else {
$r[] = urlencode($k)."=".urlencode($v);
}
}
return implode("&", $r);
}
/**
* @param string $method
* @param string $url
* @param array|null $params
* @param array|null $headers
*
* @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, $headers = null)
{
if (!$params) {
$params = array();
}
if (!$headers) {
$headers = array();
}
list($rbody, $rcode, $myApiKey) =
$this->_requestRaw($method, $url, $params, $headers);
$resp = $this->_interpretResponse($rbody, $rcode);
return array($resp, $myApiKey);
}
/**
* @param string $rbody A JSON string.
* @param int $rcode
* @param array $resp
*
* @throws Error\InvalidRequest if the error is caused by the user.
* @throws Error\Authentication if the error is caused by a lack of
* permissions.
* @throws Error\Card if the error is the error code is 402 (payment
* required)
* @throws Error\Api otherwise.
*/
public function handleApiError($rbody, $rcode, $resp)
{
if (!is_array($resp) || !isset($resp['error'])) {
$msg = "Invalid response object from API: $rbody "
. "(HTTP response code was $rcode)";
throw new Error\Api($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 Error\RateLimit($msg, $param, $rcode, $rbody, $resp);
}
// intentional fall-through
case 404:
throw new Error\InvalidRequest($msg, $param, $rcode, $rbody, $resp);
case 401:
throw new Error\Authentication($msg, $rcode, $rbody, $resp);
case 402:
throw new Error\Card($msg, $param, $code, $rcode, $rbody, $resp);
default:
throw new Error\Api($msg, $rcode, $rbody, $resp);
}
}
private function _requestRaw($method, $url, $params, $headers)
{
if (!array_key_exists($this->_apiBase, self::$_preFlight) ||
!self::$_preFlight[$this->_apiBase]) {
self::$_preFlight[$this->_apiBase] = $this->checkSslCert($this->_apiBase);
}
$myApiKey = $this->_apiKey;
if (!$myApiKey) {
$myApiKey = Stripe::$apiKey;
}
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 Error\Authentication($msg);
}
$absUrl = $this->_apiBase.$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,
);
$defaultHeaders = array(
'X-Stripe-Client-User-Agent' => json_encode($ua),
'User-Agent' => 'Stripe/v1 PhpBindings/' . Stripe::VERSION,
'Authorization' => 'Bearer ' . $myApiKey,
);
if (Stripe::$apiVersion) {
$defaultHeaders['Stripe-Version'] = Stripe::$apiVersion;
}
$hasFile = false;
$hasCurlFile = class_exists('\CURLFile', false);
foreach ($params as $k => $v) {
if (is_resource($v)) {
$hasFile = true;
$params[$k] = self::_processResourceParam($v, $hasCurlFile);
} elseif ($hasCurlFile && $v instanceof \CURLFile) {
$hasFile = true;
}
}
if ($hasFile) {
$defaultHeaders['Content-Type'] = 'multipart/form-data';
} else {
$defaultHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
}
$combinedHeaders = array_merge($defaultHeaders, $headers);
$rawHeaders = array();
foreach ($combinedHeaders as $header => $value) {
$rawHeaders[] = $header . ': ' . $value;
}
list($rbody, $rcode) = $this->_curlRequest(
$method,
$absUrl,
$rawHeaders,
$params,
$hasFile
);
return array($rbody, $rcode, $myApiKey);
}
private function _processResourceParam($resource, $hasCurlFile)
{
if (get_resource_type($resource) !== 'stream') {
throw new Error\Api(
'Attempted to upload a resource that is not a stream'
);
}
$metaData = stream_get_meta_data($resource);
if ($metaData['wrapper_type'] !== 'plainfile') {
throw new Error\Api(
'Only plainfile resource streams are supported'
);
}
if ($hasCurlFile) {
// We don't have the filename or mimetype, but the API doesn't care
return new \CURLFile($metaData['uri']);
} else {
return '@'.$metaData['uri'];
}
}
private function _interpretResponse($rbody, $rcode)
{
try {
$resp = json_decode($rbody, true);
} catch (Exception $e) {
$msg = "Invalid response body from API: $rbody "
. "(HTTP response code was $rcode)";
throw new Error\Api($msg, $rcode, $rbody);
}
if ($rcode < 200 || $rcode >= 300) {
$this->handleApiError($rbody, $rcode, $resp);
}
return $resp;
}
private function _curlRequest($method, $absUrl, $headers, $params, $hasFile)
{
$curl = curl_init();
$method = strtolower($method);
$opts = array();
if ($method == 'get') {
if ($hasFile) {
throw new Error\Api(
"Issuing a GET request with a file parameter"
);
}
$opts[CURLOPT_HTTPGET] = 1;
if (count($params) > 0) {
$encoded = self::encode($params);
$absUrl = "$absUrl?$encoded";
}
} elseif ($method == 'post') {
$opts[CURLOPT_POST] = 1;
$opts[CURLOPT_POSTFIELDS] = $hasFile ? $params : self::encode($params);
} elseif ($method == 'delete') {
$opts[CURLOPT_CUSTOMREQUEST] = 'DELETE';
if (count($params) > 0) {
$encoded = self::encode($params);
$absUrl = "$absUrl?$encoded";
}
} else {
throw new Error\Api("Unrecognized method $method");
}
$absUrl = self::utf8($absUrl);
$opts[CURLOPT_URL] = $absUrl;
$opts[CURLOPT_RETURNTRANSFER] = true;
$opts[CURLOPT_CONNECTTIMEOUT] = 30;
$opts[CURLOPT_TIMEOUT] = 80;
$opts[CURLOPT_RETURNTRANSFER] = true;
$opts[CURLOPT_HTTPHEADER] = $headers;
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 ||
$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, $cert);
$rbody = curl_exec($curl);
}
if ($rbody === false) {
$errno = curl_errno($curl);
$message = curl_error($curl);
curl_close($curl);
$this->handleCurlError($errno, $message);
}
$rcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
return array($rbody, $rcode);
}
/**
* @param number $errno
* @param string $message
* @throws ApiConnectionError
*/
public function handleCurlError($errno, $message)
{
$apiBase = $this->_apiBase;
switch ($errno) {
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";
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,";
break;
default:
$msg = "Unexpected error communicating with Stripe. "
. "If this problem persists,";
}
$msg .= " let us know at support@stripe.com.";
$msg .= "\n\n(Network error [errno $errno]: $message)";
throw new Error\ApiConnection($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 (!function_exists('stream_context_get_params') ||
!function_exists('stream_socket_enable_crypto')) {
error_log(
'Warning: This version of PHP does not support checking SSL ' .
'certificates 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 && $errno !== null) || $result === false) {
throw new Error\ApiConnection(
'Could not connect to Stripe (' . $url . '). 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 Error\ApiConnection(
'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__) . '/../../../ssl/ca-certificates.crt';
}
}

View File

@ -0,0 +1,161 @@
<?php
namespace Stripe;
abstract class ApiResource extends Object
{
private static $HEADERS_TO_PERSIST = array('Stripe-Account' => true, 'Stripe-Version' => true);
public static function baseUrl()
{
return Stripe::$apiBase;
}
/**
* @return ApiResource The refreshed resource.
*/
public function refresh()
{
$requestor = new ApiRequestor($this->_opts->apiKey, static::baseUrl());
$url = $this->instanceUrl();
list($response, $this->_opts->apiKey) = $requestor->request(
'get',
$url,
$this->_retrieveOptions,
$this->_opts->headers
);
$this->refreshFrom($response, $this->_opts);
return $this;
}
/**
* @return string The name of the class, with namespacing and underscores
* stripped.
*/
public static function className()
{
$class = get_called_class();
// Useful for namespaces: Foo\Charge
if ($postfixNamespaces = strrchr($class, '\\')) {
$class = substr($postfixNamespaces, 1);
}
// Useful for underscored 'namespaces': Foo_Charge
if ($postfixFakeNamespaces = strrchr($class, '')) {
$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;
}
/**
* @return string The endpoint URL for the given class.
*/
public static function classUrl()
{
$base = static::className();
return "/v1/${base}s";
}
/**
* @return string The full API URL for this API resource.
*/
public function instanceUrl()
{
$id = $this['id'];
if ($id === null) {
$class = get_called_class();
$message = "Could not determine which URL to request: "
. "$class instance has invalid ID: $id";
throw new Error\InvalidRequest($message, null);
}
$id = ApiRequestor::utf8($id);
$base = static::classUrl();
$extn = urlencode($id);
return "$base/$extn";
}
private static function _validateParams($params = null)
{
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: \"Stripe\\Charge::create(array('amount' => 100, "
. "'currency' => 'usd', 'card' => array('number' => "
. "4242424242424242, 'exp_month' => 5, 'exp_year' => 2015)))\")";
throw new Error\Api($message);
}
}
protected function _request($method, $url, $params = array(), $options = null)
{
$opts = $this->_opts->merge($options);
return static::_staticRequest($method, $url, $params, $opts);
}
protected static function _staticRequest($method, $url, $params, $options)
{
$opts = Util\RequestOptions::parse($options);
$requestor = new ApiRequestor($opts->apiKey, static::baseUrl());
list($response, $opts->apiKey) = $requestor->request($method, $url, $params, $opts->headers);
foreach ($opts->headers as $k => $v) {
if (!array_key_exists($k, self::$HEADERS_TO_PERSIST)) {
unset($opts->headers[$k]);
}
}
return array($response, $opts);
}
protected static function _retrieve($id, $options = null)
{
$opts = Util\RequestOptions::parse($options);
$instance = new static($id, $opts);
$instance->refresh();
return $instance;
}
protected static function _all($params = null, $options = null)
{
self::_validateParams($params);
$url = static::classUrl();
list($response, $opts) = static::_staticRequest('get', $url, $params, $options);
return Util\Util::convertToStripeObject($response, $opts);
}
protected static function _create($params = null, $options = null)
{
self::_validateParams($params);
$base = static::baseUrl();
$url = static::classUrl();
list($response, $opts) = static::_staticRequest('post', $url, $params, $options);
return Util\Util::convertToStripeObject($response, $opts);
}
protected function _save($options = null)
{
$params = $this->serializeParameters();
if (count($params) > 0) {
$url = $this->instanceUrl();
list($response, $opts) = $this->_request('post', $url, $params, $options);
$this->refreshFrom($response, $opts);
}
return $this;
}
protected function _delete($params = null, $options = null)
{
self::_validateParams($params);
$url = $this->instanceUrl();
list($response, $opts) = $this->_request('delete', $url, $params, $options);
$this->refreshFrom($response, $opts);
return $this;
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace Stripe;
class ApplicationFee extends 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()
{
return 'application_fee';
}
/**
* @param string $id The ID of the application fee to retrieve.
* @param array|string|null $opts
*
* @return ApplicationFee
*/
public static function retrieve($id, $opts = null)
{
return self::_retrieve($id, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return ApplicationFee[]
*/
public static function all($params = null, $opts = null)
{
return self::_all($params, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return ApplicationFee The refunded application fee.
*/
public function refund($params = null, $opts = null)
{
$url = $this->instanceUrl() . '/refund';
list($response, $opts) = $this->_request('post', $url, $params, $opts);
$this->refreshFrom($response, $opts);
return $this;
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace Stripe;
class ApplicationFeeRefund extends ApiResource
{
/**
* @return string The API URL for this Stripe refund.
*/
public function instanceUrl()
{
$id = $this['id'];
$fee = $this['fee'];
if (!$id) {
throw new Error\InvalidRequest(
"Could not determine which URL to request: " .
"class instance has invalid ID: $id",
null
);
}
$id = ApiRequestor::utf8($id);
$fee = ApiRequestor::utf8($fee);
$base = ApplicationFee::classUrl();
$feeExtn = urlencode($fee);
$extn = urlencode($id);
return "$base/$feeExtn/refunds/$extn";
}
/**
* @param array|string|null $opts
*
* @return ApplicationFeeRefund The saved refund.
*/
public function save($opts = null)
{
return $this->_save($opts);
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace Stripe;
// e.g. metadata on Stripe objects.
class AttachedObject extends 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,16 @@
<?php
namespace Stripe;
class Balance extends SingletonApiResource
{
/**
* @param array|string|null $opts
*
* @return Balance
*/
public static function retrieve($opts = null)
{
return self::_singletonRetrieve($opts);
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace Stripe;
class BalanceTransaction extends ApiResource
{
/**
* @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()
{
return "/v1/balance/history";
}
/**
* @param string $id The ID of the balance transaction to retrieve.
* @param array|string|null $opts
*
* @return BalanceTransaction
*/
public static function retrieve($id, $opts = null)
{
return self::_retrieve($id, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return array An array of BalanceTransactions.
*/
public static function all($params = null, $opts = null)
{
return self::_all($params, $opts);
}
}

View File

@ -0,0 +1,97 @@
<?php
namespace Stripe;
class BitcoinReceiver extends ApiResource
{
/**
* @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()
{
return "/v1/bitcoin/receivers";
}
/**
* @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 Error\InvalidRequest($msg, null);
}
$id = ApiRequestor::utf8($id);
$extn = urlencode($id);
if (!$this['customer']) {
$base = BitcoinReceiver::classUrl();
return "$base/$extn";
} else {
$base = Customer::classUrl();
$parent = ApiRequestor::utf8($this['customer']);
$parentExtn = urlencode($parent);
return "$base/$parentExtn/sources/$extn";
}
}
/**
* @param string $id The ID of the Bitcoin Receiver to retrieve.
* @param array|string|null $opts
*
* @return BitcoinReceiver
*/
public static function retrieve($id, $opts = null)
{
return self::_retrieve($id, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return BitcoinReceiver[].
*/
public static function all($params = null, $opts = null)
{
return self::_all($params, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return BitcoinReceiver The created Bitcoin Receiver item.
*/
public static function create($params = null, $opts = null)
{
return self::_create($params, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return BitcoinReceiver The deleted Bitcoin Receiver item.
*/
public function delete($params = null, $opts = null)
{
return $this->_delete($params, $opts);
}
/**
* @param array|string|null $opts
*
* @return BitcoinReceiver The saved Bitcoin Receiver item.
*/
public function save($opts = null)
{
return $this->_save($opts);
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace Stripe;
class BitcoinTransaction extends ApiResource
{
}

View File

@ -0,0 +1,46 @@
<?php
namespace Stripe;
/**
* Bootstrapper for Stripe does autoloading and resource initialization.
*/
class Bootstrap
{
const DIR_SEPARATOR = DIRECTORY_SEPARATOR;
const NAMESPACE_SEPARATOR = '\\';
public static $initialized = false;
public static function init()
{
spl_autoload_register(array('\Stripe\Bootstrap', 'autoload'));
}
public static function autoload($classname)
{
self::_autoload(dirname(dirname(__FILE__)), $classname);
}
public static function pharInit()
{
spl_autoload_register(array('\Stripe\Bootstrap', 'pharAutoload'));
}
public static function pharAutoload($classname)
{
self::_autoload('phar://Stripe.phar', $classname);
}
private static function _autoload($base, $classname)
{
$parts = explode(self::NAMESPACE_SEPARATOR, $classname);
$path = $base . self::DIR_SEPARATOR. implode(self::DIR_SEPARATOR, $parts) . '.php';
if (file_exists($path)) {
require_once($path);
}
}
}

59
include/library/Stripe/Card.php Executable file
View File

@ -0,0 +1,59 @@
<?php
namespace Stripe;
class Card extends ApiResource
{
/**
* @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 Error\InvalidRequest($msg, null);
}
if (isset($this['customer'])) {
$parent = $this['customer'];
$base = Customer::classUrl();
} elseif (isset($this['recipient'])) {
$parent = $this['recipient'];
$base = Recipient::classUrl();
} else {
return null;
}
$parent = ApiRequestor::utf8($parent);
$id = ApiRequestor::utf8($id);
$parentExtn = urlencode($parent);
$extn = urlencode($id);
return "$base/$parentExtn/cards/$extn";
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Card The deleted card.
*/
public function delete($params = null, $opts = null)
{
return $this->_delete($params, $opts);
}
/**
* @param array|string|null $opts
*
* @return Card The saved card.
*/
public function save($opts = null)
{
return $this->_save($opts);
}
}

132
include/library/Stripe/Charge.php Executable file
View File

@ -0,0 +1,132 @@
<?php
namespace Stripe;
class Charge extends ApiResource
{
/**
* @param string $id The ID of the charge to retrieve.
* @param array|string|null $options
*
* @return Charge
*/
public static function retrieve($id, $options = null)
{
return self::_retrieve($id, $options);
}
/**
* @param array|null $params
* @param array|string|null $options
*
* @return array An array of Charges.
*/
public static function all($params = null, $options = null)
{
return self::_all($params, $options);
}
/**
* @param array|null $params
* @param array|string|null $options
*
* @return Charge The created charge.
*/
public static function create($params = null, $options = null)
{
return self::_create($params, $options);
}
/**
* @param array|string|null $options
*
* @return Charge The saved charge.
*/
public function save($options = null)
{
return $this->_save($options);
}
/**
* @param array|null $params
* @param array|string|null $options
*
* @return Charge The refunded charge.
*/
public function refund($params = null, $options = null)
{
$url = $this->instanceUrl() . '/refund';
list($response, $opts) = $this->request('post', $url, $params, $options);
$this->refreshFrom($response, $opts);
return $this;
}
/**
* @param array|null $params
* @param array|string|null $options
*
* @return Charge The captured charge.
*/
public function capture($params = null, $options = null)
{
$url = $this->instanceUrl() . '/capture';
list($response, $opts) = $this->_request('post', $url, $params, $options);
$this->refreshFrom($response, $opts);
return $this;
}
/**
* @param array|null $params
* @param array|string|null $options
*
* @return array The updated dispute.
*/
public function updateDispute($params = null, $options = null)
{
$url = $this->instanceUrl() . '/dispute';
list($response, $opts) = $this->_request('post', $url, $params, $options);
$this->refreshFrom(array('dispute' => $response), $opts, true);
return $this->dispute;
}
/**
* @param array|string|null $options
*
* @return Charge The updated charge.
*/
public function closeDispute($options = null)
{
$url = $this->instanceUrl() . '/dispute/close';
list($response, $opts) = $this->_request('post', $url, null, $options);
$this->refreshFrom($response, $opts);
return $this;
}
/**
* @param array|string|null $opts
*
* @return Charge The updated charge.
*/
public function markAsFraudulent($opts = null)
{
$params = array('fraud_details' => array('user_report' => 'fraudulent'));
$url = $this->instanceUrl();
list($response, $opts) = $this->_request('post', $url, $params, $opts);
$this->refreshFrom($response, $opts);
return $this;
}
/**
* @param array|string|null $opts
*
* @return Charge The updated charge.
*/
public function markAsSafe($opts = null)
{
$params = array('fraud_details' => array('user_report' => 'safe'));
$url = $this->instanceUrl();
list($response, $opts) = $this->_request('post', $url, $params, $opts);
$this->refreshFrom($response, $opts);
return $this;
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace Stripe;
class Collection extends ApiResource
{
public function all($params = null, $opts = null)
{
list($url, $params) = $this->extractPathAndUpdateParams($params);
list($response, $opts) = $this->_request('get', $url, $params, $opts);
return Util\Util::convertToStripeObject($response, $opts);
}
public function create($params = null, $opts = null)
{
list($url, $params) = $this->extractPathAndUpdateParams($params);
list($response, $opts) = $this->_request('post', $url, $params, $opts);
return Util\Util::convertToStripeObject($response, $opts);
}
public function retrieve($id, $params = null, $opts = null)
{
list($url, $params) = $this->extractPathAndUpdateParams($params);
$id = ApiRequestor::utf8($id);
$extn = urlencode($id);
list($response, $opts) = $this->_request(
'get',
"$url/$extn",
$params,
$opts
);
return Util\Util::convertToStripeObject($response, $opts);
}
private function extractPathAndUpdateParams($params)
{
$url = parse_url($this->url);
if (!isset($url['path'])) {
throw new Error\Api("Could not parse list url into parts: $url");
}
if (isset($url['query'])) {
// If the URL contains a query param, parse it out into $params so they
// don't interact weirdly with each other.
$query = array();
parse_str($url['query'], $query);
// PHP 5.2 doesn't support the ?: operator :(
$params = array_merge($params ? $params : array(), $query);
}
return array($url['path'], $params);
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace Stripe;
class Coupon extends ApiResource
{
/**
* @param string $id The ID of the coupon to retrieve.
* @param array|string|null $opts
*
* @return Coupon
*/
public static function retrieve($id, $opts = null)
{
return self::_retrieve($id, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Coupon The created coupon.
*/
public static function create($params = null, $opts = null)
{
return self::_create($params, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Coupon The deleted coupon.
*/
public function delete($params = null, $opts = null)
{
return $this->_delete($params, $opts);
}
/**
* @param array|string|null $opts
*
* @return Coupon The saved coupon.
*/
public function save($opts = null)
{
return $this->_save($opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Coupon[]
*/
public static function all($params = null, $opts = null)
{
return self::_all($params, $opts);
}
}

View File

@ -0,0 +1,158 @@
<?php
namespace Stripe;
class Customer extends ApiResource
{
/**
* @param string $id The ID of the customer to retrieve.
* @param array|string|null $opts
*
* @return Customer
*/
public static function retrieve($id, $opts = null)
{
return self::_retrieve($id, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return array An array of Customers.
*/
public static function all($params = null, $opts = null)
{
return self::_all($params, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Customer The created customer.
*/
public static function create($params = null, $opts = null)
{
return self::_create($params, $opts);
}
/**
* @param array|string|null $opts
*
* @return Customer The saved customer.
*/
public function save($opts = null)
{
return $this->_save($opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Customer The deleted customer.
*/
public function delete($params = null, $opts = null)
{
return $this->_delete($params, $opts);
}
/**
* @param array|null $params
*
* @return InvoiceItem The resulting invoice item.
*/
public function addInvoiceItem($params = null)
{
if (!$params) {
$params = array();
}
$params['customer'] = $this->id;
$ii = InvoiceItem::create($params, $this->_opts);
return $ii;
}
/**
* @param array|null $params
*
* @return array An array of the customer's Invoices.
*/
public function invoices($params = null)
{
if (!$params) {
$params = array();
}
$params['customer'] = $this->id;
$invoices = Invoice::all($params, $this->_opts);
return $invoices;
}
/**
* @param array|null $params
*
* @return array An array of the customer's InvoiceItems.
*/
public function invoiceItems($params = null)
{
if (!$params) {
$params = array();
}
$params['customer'] = $this->id;
$iis = InvoiceItem::all($params, $this->_opts);
return $iis;
}
/**
* @param array|null $params
*
* @return array An array of the customer's Charges.
*/
public function charges($params = null)
{
if (!$params) {
$params = array();
}
$params['customer'] = $this->id;
$charges = Charge::all($params, $this->_opts);
return $charges;
}
/**
* @param array|null $params
*
* @return Subscription The updated subscription.
*/
public function updateSubscription($params = null)
{
$url = $this->instanceUrl() . '/subscription';
list($response, $opts) = $this->_request('post', $url, $params);
$this->refreshFrom(array('subscription' => $response), $opts, true);
return $this->subscription;
}
/**
* @param array|null $params
*
* @return Subscription The cancelled subscription.
*/
public function cancelSubscription($params = null)
{
$url = $this->instanceUrl() . '/subscription';
list($response, $opts) = $this->_request('delete', $url, $params);
$this->refreshFrom(array('subscription' => $response), $opts, true);
return $this->subscription;
}
/**
* @param array|null $params
*
* @return Customer The updated customer.
*/
public function deleteDiscount()
{
$url = $this->instanceUrl() . '/discount';
list($response, $opts) = $this->_request('delete', $url);
$this->refreshFrom(array('discount' => null), $opts, true);
}
}

View File

@ -0,0 +1,7 @@
<?php
namespace Stripe\Error;
class Api extends Base
{
}

View File

@ -0,0 +1,7 @@
<?php
namespace Stripe\Error;
class ApiConnection extends Base
{
}

View File

@ -0,0 +1,7 @@
<?php
namespace Stripe\Error;
class Authentication extends Base
{
}

View File

@ -0,0 +1,35 @@
<?php
namespace Stripe\Error;
use Exception;
abstract class Base extends Exception
{
public function __construct(
$message,
$httpStatus = null,
$httpBody = null,
$jsonBody = null
) {
parent::__construct($message);
$this->httpStatus = $httpStatus;
$this->httpBody = $httpBody;
$this->jsonBody = $jsonBody;
}
public function getHttpStatus()
{
return $this->httpStatus;
}
public function getHttpBody()
{
return $this->httpBody;
}
public function getJsonBody()
{
return $this->jsonBody;
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Stripe\Error;
class Card extends Base
{
public function __construct(
$message,
$param,
$code,
$httpStatus,
$httpBody,
$jsonBody
) {
parent::__construct($message, $httpStatus, $httpBody, $jsonBody);
$this->param = $param;
$this->code = $code;
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Stripe\Error;
class InvalidRequest extends Base
{
public function __construct(
$message,
$param,
$httpStatus = null,
$httpBody = null,
$jsonBody = null
) {
parent::__construct($message, $httpStatus, $httpBody, $jsonBody);
$this->param = $param;
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Stripe\Error;
class RateLimit extends InvalidRequest
{
public function __construct(
$message,
$param,
$httpStatus = null,
$httpBody = null,
$jsonBody = null
) {
parent::__construct($message, $httpStatus, $httpBody, $jsonBody);
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace Stripe;
class Event extends ApiResource
{
/**
* @param string $id The ID of the event to retrieve.
* @param array|string|null $opts
*
* @return Event
*/
public static function retrieve($id, $opts = null)
{
return self::_retrieve($id, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Event[]
*/
public static function all($params = null, $opts = null)
{
return self::_all($params, $opts);
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace Stripe;
class FileUpload extends ApiResource
{
public static function baseUrl()
{
return Stripe::$apiUploadBase;
}
public static function className()
{
return 'file';
}
/**
* @param string $id The ID of the file upload to retrieve.
* @param array|string|null $opts
*
* @return FileUpload
*/
public static function retrieve($id, $opts = null)
{
return self::_retrieve($id, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return FileUpload The created file upload.
*/
public static function create($params = null, $opts = null)
{
return self::_create($params, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return FileUpload[]
*/
public static function all($params = null, $opts = null)
{
return self::_all($params, $opts);
}
}

View File

@ -0,0 +1,73 @@
<?php
namespace Stripe;
class Invoice extends ApiResource
{
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Invoice The created invoice.
*/
public static function create($params = null, $opts = null)
{
return self::_create($params, $opts);
}
/**
* @param string $id The ID of the invoice to retrieve.
* @param array|string|null $opts
*
* @return Invoice
*/
public static function retrieve($id, $opts = null)
{
return self::_retrieve($id, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Invoice[]
*/
public static function all($params = null, $opts = null)
{
return self::_all($params, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Invoice The upcoming invoice.
*/
public static function upcoming($params = null, $opts = null)
{
$url = static::classUrl() . '/upcoming';
list($response, $opts) = static::_staticRequest('get', $url, $params, $opts);
return Util\Util::convertToStripeObject($response, $opts);
}
/**
* @param array|string|null $opts
*
* @return Invoice The saved invoice.
*/
public function save($opts = null)
{
return $this->_save($opts);
}
/**
* @return Invoice The paid invoice.
*/
public function pay($opts = null)
{
$url = $this->instanceUrl() . '/pay';
list($response, $opts) = $this->_request('post', $url, null, $opts);
$this->refreshFrom($response, $opts);
return $this;
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace Stripe;
class InvoiceItem extends ApiResource
{
/**
* @param string $id The ID of the invoice item to retrieve.
* @param array|string|null $opts
*
* @return InvoiceItem
*/
public static function retrieve($id, $opts = null)
{
return self::_retrieve($id, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return InvoiceItem[]
*/
public static function all($params = null, $opts = null)
{
return self::_all($params, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return InvoiceItem The created invoice item.
*/
public static function create($params = null, $opts = null)
{
return self::_create($params, $opts);
}
/**
* @param array|string|null $opts
*
* @return InvoiceItem The saved invoice item.
*/
public function save($opts = null)
{
return $this->_save($opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return InvoiceItem The deleted invoice item.
*/
public function delete($params = null, $opts = null)
{
return $this->_delete($params, $opts);
}
}

247
include/library/Stripe/Object.php Executable file
View File

@ -0,0 +1,247 @@
<?php
namespace Stripe;
use ArrayAccess;
use InvalidArgumentException;
class Object implements ArrayAccess
{
/**
* @var 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 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 Util\Set(array('_opts', 'id'));
self::$nestedUpdatableAttributes = new Util\Set(array('metadata'));
}
protected $_opts;
protected $_values;
protected $_unsavedValues;
protected $_transientValues;
protected $_retrieveOptions;
public function __construct($id = null, $opts = null)
{
$this->_opts = $opts ? $opts : new Util\RequestOptions();
$this->_values = array();
$this->_unsavedValues = new Util\Set();
$this->_transientValues = new Util\Set();
$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)
{
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)
{
return isset($this->_values[$k]);
}
public function __unset($k)
{
unset($this->_values[$k]);
$this->_transientValues->add($k);
$this->_unsavedValues->discard($k);
}
public function __get($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));
$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);
error_log("Stripe Notice: Undefined property of $class instance: $k");
return null;
}
}
// ArrayAccess methods
public function offsetSet($k, $v)
{
$this->$k = $v;
}
public function offsetExists($k)
{
return array_key_exists($k, $this->_values);
}
public function offsetUnset($k)
{
unset($this->$k);
}
public function offsetGet($k)
{
return array_key_exists($k, $this->_values) ? $this->_values[$k] : null;
}
public function keys()
{
return array_keys($this->_values);
}
/**
* This unfortunately needs to be public to be used in Util\Util
*
* @param array $values
* @param array $opts
*
* @return Object The object constructed from the given values.
*/
public static function constructFrom($values, $opts)
{
$obj = new static(isset($values['id']) ? $values['id'] : null);
$obj->refreshFrom($values, $opts);
return $obj;
}
/**
* Refreshes this object using the provided values.
*
* @param array $values
* @param array $opts
* @param boolean $partial Defaults to false.
*/
public function refreshFrom($values, $opts, $partial = false)
{
$this->_opts = $opts;
// 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) {
$removed = new Util\Set();
} else {
$removed = array_diff(array_keys($this->_values), array_keys($values));
}
foreach ($removed as $k) {
if (self::$permanentAttributes->includes($k)) {
continue;
}
unset($this->$k);
}
foreach ($values as $k => $v) {
if (self::$permanentAttributes->includes($k) && isset($this[$k])) {
continue;
}
if (self::$nestedUpdatableAttributes->includes($k) && is_array($v)) {
$this->_values[$k] = AttachedObject::constructFrom($v, $opts);
} else {
$this->_values[$k] = Util\Util::convertToStripeObject($v, $opts);
}
$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 Object) {
$params[$property] = $this->$property->serializeParameters();
}
}
return $params;
}
public function __toJSON()
{
if (defined('JSON_PRETTY_PRINT')) {
return json_encode($this->__toArray(true), JSON_PRETTY_PRINT);
} else {
return json_encode($this->__toArray(true));
}
}
public function __toString()
{
$class = get_class($this);
return $class . ' JSON: ' . $this->__toJSON();
}
public function __toArray($recursive = false)
{
if ($recursive) {
return Util\Util::convertStripeObjectToArray($this->_values);
} else {
return $this->_values;
}
}
}
Object::init();

60
include/library/Stripe/Plan.php Executable file
View File

@ -0,0 +1,60 @@
<?php
namespace Stripe;
class Plan extends ApiResource
{
/**
* @param string $id The ID of the plan to retrieve.
* @param array|string|null $opts
*
* @return Plan
*/
public static function retrieve($id, $opts = null)
{
return self::_retrieve($id, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Plan The created plan.
*/
public static function create($params = null, $opts = null)
{
return self::_create($params, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Plan The deleted plan.
*/
public function delete($params = null, $opts = null)
{
return $this->_delete($params, $opts);
}
/**
* @param array|string|null $opts
*
* @return Plan The saved plan.
*/
public function save($opts = null)
{
return $this->_save($opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Plan[]
*/
public static function all($params = null, $opts = null)
{
return self::_all($params, $opts);
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace Stripe;
class Recipient extends ApiResource
{
/**
* @param string $id The ID of the recipient to retrieve.
* @param array|string|null $opts
*
* @return Recipient
*/
public static function retrieve($id, $opts = null)
{
return self::_retrieve($id, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Recipients[]
*/
public static function all($params = null, $opts = null)
{
return self::_all($params, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Recipient The created recipient.
*/
public static function create($params = null, $opts = null)
{
return self::_create($params, $opts);
}
/**
* @param array|string|null $opts
*
* @return Recipient The saved recipient.
*/
public function save($opts = null)
{
return $this->_save($opts);
}
/**
* @param array|null $params
*
* @return Recipient The deleted recipient.
*/
public function delete($params = null, $opts = null)
{
return $this->_delete($params, $opts);
}
/**
* @param array|null $params
*
* @return array An array of the recipient's Transfers.
*/
public function transfers($params = null)
{
if ($params === null) {
$params = array();
}
$params['recipient'] = $this->id;
$transfers = Transfer::all($params, $this->_opts);
return $transfers;
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace Stripe;
class Refund extends ApiResource
{
/**
* @return string The API URL for this Stripe refund.
*/
public function instanceUrl()
{
$id = $this['id'];
$charge = $this['charge'];
if (!$id) {
throw new Error\InvalidRequest(
"Could not determine which URL to request: " .
"class instance has invalid ID: $id",
null
);
}
$id = ApiRequestor::utf8($id);
$charge = ApiRequestor::utf8($charge);
$base = Charge::classUrl();
$chargeExtn = urlencode($charge);
$extn = urlencode($id);
return "$base/$chargeExtn/refunds/$extn";
}
/**
* @param array|string|null $opts
*
* @return Refund The saved refund.
*/
public function save($opts = null)
{
return $this->_save($opts);
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Stripe;
abstract class SingletonApiResource extends ApiResource
{
protected static function _singletonRetrieve($options = null)
{
$opts = Util\RequestOptions::parse($options);
$instance = new static(null, $opts);
$instance->refresh();
return $instance;
}
/**
* @return string The endpoint associated with this singleton class.
*/
public static function classUrl()
{
$base = static::className();
return "/v1/${base}";
}
/**
* @return string The endpoint associated with this singleton API resource.
*/
public function instanceUrl()
{
return static::classUrl();
}
}

View File

@ -0,0 +1,74 @@
<?php
namespace Stripe;
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 The base URL for the Stripe API uploads endpoint.
public static $apiUploadBase = 'https://uploads.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 = '2.1.1';
/**
* @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,59 @@
<?php
namespace Stripe;
class Subscription extends ApiResource
{
/**
* @return string The API URL for this Stripe subscription.
*/
public function instanceUrl()
{
$id = $this['id'];
$customer = $this['customer'];
if (!$id) {
throw new Error\InvalidRequest(
"Could not determine which URL to request: " .
"class instance has invalid ID: $id",
null
);
}
$id = ApiRequestor::utf8($id);
$customer = ApiRequestor::utf8($customer);
$base = Customer::classUrl();
$customerExtn = urlencode($customer);
$extn = urlencode($id);
return "$base/$customerExtn/subscriptions/$extn";
}
/**
* @param array|null $params
*
* @return Subscription The deleted subscription.
*/
public function cancel($params = null, $opts = null)
{
return $this->_delete($params, $opts);
}
/**
* @param array|string|null $opts
*
* @return Subscription The saved subscription.
*/
public function save($opts = null)
{
return $this->_save($opts);
}
/**
* @return Subscription The updated subscription.
*/
public function deleteDiscount()
{
$url = $this->instanceUrl() . '/discount';
list($response, $opts) = $this->_request('delete', $url);
$this->refreshFrom(array('discount' => null), $opts, true);
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace Stripe;
class Token extends ApiResource
{
/**
* @param string $id The ID of the token to retrieve.
* @param array|string|null $opts
*
* @return Token
*/
public static function retrieve($id, $opts = null)
{
return self::_retrieve($id, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Token The created token.
*/
public static function create($params = null, $opts = null)
{
return self::_create($params, $opts);
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace Stripe;
class Transfer extends ApiResource
{
/**
* @param string $id The ID of the transfer to retrieve.
* @param array|string|null $opts
*
* @return Transfer
*/
public static function retrieve($id, $opts = null)
{
return self::_retrieve($id, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Transfer[]
*/
public static function all($params = null, $opts = null)
{
return self::_all($params, $opts);
}
/**
* @param array|null $params
* @param array|string|null $opts
*
* @return Transfer The created transfer.
*/
public static function create($params = null, $opts = null)
{
return self::_create($params, $opts);
}
/**
* @return TransferReversal The created transfer reversal.
*/
public function reverse($params = null, $opts = null)
{
$url = $this->instanceUrl() . '/reversals';
list($response, $opts) = $this->request('post', $url, $params, $options);
$this->refreshFrom($response, $opts);
return $this;
}
/**
* @return Transfer The canceled transfer.
*/
public function cancel()
{
$url = $this->instanceUrl() . '/cancel';
list($response, $opts) = $this->_request('post', $url);
$this->refreshFrom($response, $opts);
return $this;
}
/**
* @param array|string|null $opts
*
* @return Transfer The saved transfer.
*/
public function save($opts = null)
{
return $this->_save($opts);
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace Stripe;
class TransferReversal extends ApiResource
{
/**
* @return string The API URL for this Stripe transfer reversal.
*/
public function instanceUrl()
{
$id = $this['id'];
$transfer = $this['transfer'];
if (!$id) {
throw new Error\InvalidRequest(
"Could not determine which URL to request: " .
"class instance has invalid ID: $id",
null
);
}
$id = ApiRequestor::utf8($id);
$transfer = ApiRequestor::utf8($transfer);
$base = Transfer::classUrl();
$transferExtn = urlencode($transfer);
$extn = urlencode($id);
return "$base/$transferExtn/reversals/$extn";
}
/**
* @param array|string|null $opts
*
* @return TransferReversal The saved reversal.
*/
public function save($opts = null)
{
return $this->_save($opts);
}
}

View File

@ -0,0 +1,79 @@
<?php
namespace Stripe\Util;
use Stripe\Error;
class RequestOptions
{
public $headers;
public $apiKey;
public function __construct($key = null, $headers = array())
{
$this->apiKey = $key;
$this->headers = $headers;
}
/**
* Unpacks an options array and merges it into the existing RequestOptions
* object.
* @param array|string|null $options a key => value array
*
* @return RequestOptions
*/
public function merge($options)
{
$other_options = self::parse($options);
if ($other_options->apiKey === null) {
$other_options->apiKey = $this->apiKey;
}
$other_options->headers = array_merge($this->headers, $other_options->headers);
return $other_options;
}
/**
* Unpacks an options array into an RequestOptions object
* @param array|string|null $options a key => value array
*
* @return RequestOptions
*/
public static function parse($options)
{
if ($options instanceof self) {
return $options;
}
if (is_null($options)) {
return new RequestOptions(null, array());
}
if (is_string($options)) {
return new RequestOptions($options, array());
}
if (is_array($options)) {
$headers = array();
$key = null;
if (array_key_exists('api_key', $options)) {
$key = $options['api_key'];
}
if (array_key_exists('idempotency_key', $options)) {
$headers['Idempotency-Key'] = $options['idempotency_key'];
}
if (array_key_exists('stripe_account', $options)) {
$headers['Stripe-Account'] = $options['stripe_account'];
}
if (array_key_exists('stripe_version', $options)) {
$headers['Stripe-Version'] = $options['stripe_version'];
}
return new RequestOptions($key, $headers);
}
$message = 'The second argument to Stripe API method calls is an '
. 'optional per-request apiKey, which must be a string, or '
. 'per-request options, which must be an array. (HINT: you can set '
. 'a global apiKey by "Stripe::setApiKey(<apiKey>)")';
throw new Error\Api($message);
}
}

View File

@ -0,0 +1,44 @@
<?php
namespace Stripe\Util;
use IteratorAggregate;
use ArrayIterator;
class Set implements IteratorAggregate
{
private $_elts;
public function __construct($members = array())
{
$this->_elts = array();
foreach ($members as $item) {
$this->_elts[$item] = true;
}
}
public function includes($elt)
{
return isset($this->_elts[$elt]);
}
public function add($elt)
{
$this->_elts[$elt] = true;
}
public function discard($elt)
{
unset($this->_elts[$elt]);
}
public function toArray()
{
return array_keys($this->_elts);
}
public function getIterator()
{
return new ArrayIterator($this->toArray());
}
}

View File

@ -0,0 +1,102 @@
<?php
namespace Stripe\Util;
use Stripe\Object;
abstract class 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: generally incorrect, but it's correct given Stripe's response
foreach (array_keys($array) as $k) {
if (!is_numeric($k)) {
return false;
}
}
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 ($k[0] == '_') {
continue;
}
if ($v instanceof Object) {
$results[$k] = $v->__toArray(true);
} elseif (is_array($v)) {
$results[$k] = self::convertStripeObjectToArray($v);
} 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 array $opts
* @return Object|array
*/
public static function convertToStripeObject($resp, $opts)
{
$types = array(
'account' => 'Stripe\\Account',
'card' => 'Stripe\\Card',
'charge' => 'Stripe\\Charge',
'coupon' => 'Stripe\\Coupon',
'customer' => 'Stripe\\Customer',
'list' => 'Stripe\\Collection',
'invoice' => 'Stripe\\Invoice',
'invoiceitem' => 'Stripe\\InvoiceItem',
'event' => 'Stripe\\Event',
'file' => 'Stripe\\FileUpload',
'token' => 'Stripe\\Token',
'transfer' => 'Stripe\\Transfer',
'plan' => 'Stripe\\Plan',
'recipient' => 'Stripe\\Recipient',
'refund' => 'Stripe\\Refund',
'subscription' => 'Stripe\\Subscription',
'fee_refund' => 'Stripe\\ApplicationFeeRefund',
'bitcoin_receiver' => 'Stripe\\BitcoinReceiver',
'bitcoin_transaction' => 'Stripe\\BitcoinTransaction',
);
if (self::isList($resp)) {
$mapped = array();
foreach ($resp as $i) {
array_push($mapped, self::convertToStripeObject($i, $opts));
}
return $mapped;
} elseif (is_array($resp)) {
if (isset($resp['object']) && is_string($resp['object']) && isset($types[$resp['object']])) {
$class = $types[$resp['object']];
} else {
$class = 'Stripe\\Object';
}
return $class::constructFrom($resp, $opts);
} else {
return $resp;
}
}
}

View File

@ -3,34 +3,33 @@ console.log('App.tokenizeCard');
var processor = ( App.config.processor && App.config.processor.type ) ? App.config.processor.type : false;
console.debug('Processor: ', processor);
var legacy_card = {
card_number: card.number,
expiration_month: card.expiration_month,
expiration_year: card.expiration_year,
security_code: null
};
if (App.isPhoneGap) {
//card = legacy_card;
}
switch(processor) {
case 'stripe':
App.tokenizeCard_stripe( legacy_card, complete );
App.tokenizeCard_stripe( card, complete );
break;
case 'balanced':
App.tokenizeCard_balanced( card, complete );
break;
break;
default:
App.alert('There was an error communicating with our payment processor.');
console.log( 'Processor error::', App.config.processor );
break;
break;
}
};
App.tokenizeCard_stripe = function( card, complete ) {
var res = { status: false };
var card = { number: card.card_number, exp_month: card.expiration_month, exp_year: card.expiration_year };
var res = {
status: false
};
var card = {
number: card.number,
exp_month: card.expiration_month,
exp_year: card.expiration_year
};
Stripe.card.createToken( card , function( status, response ){
console.debug('Recieved response from stripe: ', status, response);
if ( response.error ) {
switch( response.error.code ){
case 'incorrect_number':
@ -70,7 +69,7 @@ App.tokenizeCard_stripe = function( card, complete ) {
id : response.card.id,
uri: response.id,
lastfour: response.card.last4,
card_type: response.card.type,
card_type: response.card.brand.toLowerCase(),
month: response.card.exp_month,
year: response.card.exp_year,
status : true