Skip to content

Commit cc586b0

Browse files
committed
For discussion -- Stripe integration
Summary: various stripe stuff, including - external stripe library - payment form - test controller to play with payment form, sample business logic My main questions / discussion topics are... - is the stripe PHP library too big? (ie should I write something more simple just for phabricator?) -- if its cool, what is the best way to include the client? (ie should I make it a submodule rather than the flat copy here?) - is the JS I wrote (too) ridiculous? -- particularly unhappy with the error message stuff being in JS *but* it seemed the best choice given the most juicy error messages come from the stripe JS such that the overall code complexity is lowest this way. - how should the stripe JS be included? -- flat copy like I did here? -- some sort of external? -- can we just load it off stripe servers at request time? (I like that from the "if stripe is down, stripe is down" perspective) - wasn't sure if the date control was too silly and should just be baked into the form? -- for some reason I feel like its good to be prepared to walk away from Stripe / switch providers here, though I think this is on the wrong side of pragmatic Test Plan: - played around with sample client form Reviewers: epriestley Reviewed By: epriestley CC: aran Differential Revision: https://secure.phabricator.com/D2096
1 parent 877cb13 commit cc586b0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+5710
-8
lines changed

externals/stripe-js/stripe_core.js

+17
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

externals/stripe-php/LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License
2+
3+
Copyright (c) 2010 Stripe
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

externals/stripe-php/README.rdoc

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
= Installation
2+
3+
Obtain the latest version of the Stripe PHP bindings with:
4+
5+
git clone https://github.com/stripe/stripe-php
6+
7+
To get started, add the following to your PHP script:
8+
9+
require_once("/path/to/stripe-php/lib/Stripe.php");
10+
11+
Simple usage looks like:
12+
13+
Stripe::setApiKey('d8e8fca2dc0f896fd7cb4cb0031ba249');
14+
$myCard = array('number' => '4242424242424242', 'exp_month' => 5, 'exp_year' => 2015);
15+
$charge = Stripe_Charge::create(array('card' => $myCard, 'amount' => 2000, 'currency' => 'usd'));
16+
echo $charge;
17+
18+
= Documentation
19+
20+
Please see https://stripe.com/api for up-to-date documentation.

externals/stripe-php/VERSION

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.6.3

externals/stripe-php/lib/Stripe.php

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
// Tested on PHP 5.2, 5.3
4+
5+
// This snippet (and some of the curl code) due to the Facebook SDK.
6+
if (!function_exists('curl_init')) {
7+
throw new Exception('Stripe needs the CURL PHP extension.');
8+
}
9+
if (!function_exists('json_decode')) {
10+
throw new Exception('Stripe needs the JSON PHP extension.');
11+
}
12+
13+
14+
abstract class Stripe
15+
{
16+
public static $apiKey;
17+
public static $apiBase = 'https://api.stripe.com/v1';
18+
public static $verifySslCerts = true;
19+
const VERSION = '1.6.3';
20+
21+
public static function getApiKey()
22+
{
23+
return self::$apiKey;
24+
}
25+
26+
public static function setApiKey($apiKey)
27+
{
28+
self::$apiKey = $apiKey;
29+
}
30+
31+
public static function getVerifySslCerts() {
32+
return self::$verifySslCerts;
33+
}
34+
35+
public static function setVerifySslCerts($verify) {
36+
self::$verifySslCerts = $verify;
37+
}
38+
}
39+
40+
41+
// Utilities
42+
require(dirname(__FILE__) . '/Stripe/Util.php');
43+
require(dirname(__FILE__) . '/Stripe/Util/Set.php');
44+
45+
// Errors
46+
require(dirname(__FILE__) . '/Stripe/Error.php');
47+
require(dirname(__FILE__) . '/Stripe/ApiError.php');
48+
require(dirname(__FILE__) . '/Stripe/ApiConnectionError.php');
49+
require(dirname(__FILE__) . '/Stripe/AuthenticationError.php');
50+
require(dirname(__FILE__) . '/Stripe/CardError.php');
51+
require(dirname(__FILE__) . '/Stripe/InvalidRequestError.php');
52+
53+
// Plumbing
54+
require(dirname(__FILE__) . '/Stripe/Object.php');
55+
require(dirname(__FILE__) . '/Stripe/ApiRequestor.php');
56+
require(dirname(__FILE__) . '/Stripe/ApiResource.php');
57+
58+
// Stripe API Resources
59+
require(dirname(__FILE__) . '/Stripe/Charge.php');
60+
require(dirname(__FILE__) . '/Stripe/Customer.php');
61+
require(dirname(__FILE__) . '/Stripe/Invoice.php');
62+
require(dirname(__FILE__) . '/Stripe/InvoiceItem.php');
63+
require(dirname(__FILE__) . '/Stripe/Plan.php');
64+
require(dirname(__FILE__) . '/Stripe/Token.php');
65+
require(dirname(__FILE__) . '/Stripe/Coupon.php');
66+
require(dirname(__FILE__) . '/Stripe/Event.php');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
class Stripe_ApiConnectionError extends Stripe_Error
4+
{
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
class Stripe_ApiError extends Stripe_Error
4+
{
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
<?php
2+
3+
class Stripe_ApiRequestor
4+
{
5+
public $apiKey;
6+
7+
public function __construct($apiKey=null)
8+
{
9+
$this->_apiKey = $apiKey;
10+
}
11+
12+
public static function apiUrl($url='')
13+
{
14+
$apiBase = Stripe::$apiBase;
15+
return "$apiBase$url";
16+
}
17+
18+
public static function utf8($value)
19+
{
20+
if (is_string($value))
21+
return utf8_encode($value);
22+
else
23+
return $value;
24+
}
25+
26+
private static function _encodeObjects($d)
27+
{
28+
if ($d instanceof Stripe_ApiRequestor) {
29+
return $d->id;
30+
} else if ($d === true) {
31+
return 'true';
32+
} else if ($d === false) {
33+
return 'false';
34+
} else if (is_array($d)) {
35+
$res = array();
36+
foreach ($d as $k => $v)
37+
$res[$k] = self::_encodeObjects($v);
38+
return $res;
39+
} else {
40+
return $d;
41+
}
42+
}
43+
44+
public static function encode($d)
45+
{
46+
return http_build_query($d, null, '&');
47+
}
48+
49+
public function request($meth, $url, $params=null)
50+
{
51+
if (!$params)
52+
$params = array();
53+
list($rbody, $rcode, $myApiKey) = $this->_requestRaw($meth, $url, $params);
54+
$resp = $this->_interpretResponse($rbody, $rcode);
55+
return array($resp, $myApiKey);
56+
}
57+
58+
public function handleApiError($rbody, $rcode, $resp)
59+
{
60+
if (!is_array($resp) || !isset($resp['error']))
61+
throw new Stripe_ApiError("Invalid response object from API: $rbody (HTTP response code was $rcode)", $rcode, $rbody, $resp);
62+
$error = $resp['error'];
63+
switch ($rcode) {
64+
case 400:
65+
case 404:
66+
throw new Stripe_InvalidRequestError(isset($error['message']) ? $error['message'] : null,
67+
isset($error['param']) ? $error['param'] : null,
68+
$rcode, $rbody, $resp);
69+
case 401:
70+
throw new Stripe_AuthenticationError(isset($error['message']) ? $error['message'] : null, $rcode, $rbody, $resp);
71+
case 402:
72+
throw new Stripe_CardError(isset($error['message']) ? $error['message'] : null,
73+
isset($error['param']) ? $error['param'] : null,
74+
isset($error['code']) ? $error['code'] : null,
75+
$rcode, $rbody, $resp);
76+
default:
77+
throw new Stripe_ApiError(isset($error['message']) ? $error['message'] : null, $rcode, $rbody, $resp);
78+
}
79+
}
80+
81+
private function _requestRaw($meth, $url, $params)
82+
{
83+
$myApiKey = $this->_apiKey;
84+
if (!$myApiKey)
85+
$myApiKey = Stripe::$apiKey;
86+
if (!$myApiKey)
87+
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.');
88+
89+
$absUrl = $this->apiUrl($url);
90+
$params = self::_encodeObjects($params);
91+
$langVersion = phpversion();
92+
$uname = php_uname();
93+
$ua = array('bindings_version' => Stripe::VERSION,
94+
'lang' => 'php',
95+
'lang_version' => $langVersion,
96+
'publisher' => 'stripe',
97+
'uname' => $uname);
98+
$headers = array('X-Stripe-Client-User-Agent: ' . json_encode($ua),
99+
'User-Agent: Stripe/v1 PhpBindings/' . Stripe::VERSION);
100+
list($rbody, $rcode) = $this->_curlRequest($meth, $absUrl, $headers, $params, $myApiKey);
101+
return array($rbody, $rcode, $myApiKey);
102+
}
103+
104+
private function _interpretResponse($rbody, $rcode)
105+
{
106+
try {
107+
$resp = json_decode($rbody, true);
108+
} catch (Exception $e) {
109+
throw new Stripe_ApiError("Invalid response body from API: $rbody (HTTP response code was $rcode)", $rcode, $rbody);
110+
}
111+
112+
if ($rcode < 200 || $rcode >= 300) {
113+
$this->handleApiError($rbody, $rcode, $resp);
114+
}
115+
return $resp;
116+
}
117+
118+
private function _curlRequest($meth, $absUrl, $headers, $params, $myApiKey)
119+
{
120+
$curl = curl_init();
121+
$meth = strtolower($meth);
122+
$opts = array();
123+
if ($meth == 'get') {
124+
$opts[CURLOPT_HTTPGET] = 1;
125+
if (count($params) > 0) {
126+
$encoded = self::encode($params);
127+
$absUrl = "$absUrl?$encoded";
128+
}
129+
} else if ($meth == 'post') {
130+
$opts[CURLOPT_POST] = 1;
131+
$opts[CURLOPT_POSTFIELDS] = self::encode($params);
132+
} else if ($meth == 'delete') {
133+
$opts[CURLOPT_CUSTOMREQUEST] = 'DELETE';
134+
if (count($params) > 0) {
135+
$encoded = self::encode($params);
136+
$absUrl = "$absUrl?$encoded";
137+
}
138+
} else {
139+
throw new Stripe_ApiError("Unrecognized method $meth");
140+
}
141+
142+
$absUrl = self::utf8($absUrl);
143+
$opts[CURLOPT_URL] = $absUrl;
144+
$opts[CURLOPT_RETURNTRANSFER] = true;
145+
$opts[CURLOPT_CONNECTTIMEOUT] = 30;
146+
$opts[CURLOPT_TIMEOUT] = 80;
147+
$opts[CURLOPT_RETURNTRANSFER] = true;
148+
$opts[CURLOPT_HTTPHEADER] = $headers;
149+
$opts[CURLOPT_USERPWD] = $myApiKey . ':';
150+
if (!Stripe::$verifySslCerts)
151+
$opts[CURLOPT_SSL_VERIFYPEER] = false;
152+
153+
curl_setopt_array($curl, $opts);
154+
$rbody = curl_exec($curl);
155+
156+
$errno = curl_errno($curl);
157+
if ($errno == CURLE_SSL_CACERT || $errno == CURLE_SSL_PEER_CERTIFICATE) {
158+
array_push($headers, 'X-Stripe-Client-Info: {"ca":"using Stripe-supplied CA bundle"}');
159+
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
160+
curl_setopt($curl, CURLOPT_CAINFO,
161+
dirname(__FILE__) . '/../data/ca-certificates.crt');
162+
$rbody = curl_exec($curl);
163+
}
164+
165+
if ($rbody === false) {
166+
$errno = curl_errno($curl);
167+
$message = curl_error($curl);
168+
curl_close($curl);
169+
$this->handleCurlError($errno, $message);
170+
}
171+
172+
$rcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
173+
curl_close($curl);
174+
return array($rbody, $rcode);
175+
}
176+
177+
public function handleCurlError($errno, $message)
178+
{
179+
$apiBase = Stripe::$apiBase;
180+
switch ($errno) {
181+
case CURLE_COULDNT_CONNECT:
182+
case CURLE_COULDNT_RESOLVE_HOST:
183+
case CURLE_OPERATION_TIMEOUTED:
184+
$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.";
185+
break;
186+
case CURLE_SSL_CACERT:
187+
case CURLE_SSL_PEER_CERTIFICATE:
188+
$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.";
189+
break;
190+
default:
191+
$msg = "Unexpected error communicating with Stripe. If this problem persists, let us know at support@stripe.com.";
192+
}
193+
194+
$msg .= "\n\n(Network error: $message)";
195+
throw new Stripe_ApiConnectionError($msg);
196+
}
197+
}

0 commit comments

Comments
 (0)