mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-30 17:30:59 +01:00
cc586b0afa
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
197 lines
6.5 KiB
PHP
197 lines
6.5 KiB
PHP
<?php
|
|
|
|
class Stripe_ApiRequestor
|
|
{
|
|
public $apiKey;
|
|
|
|
public function __construct($apiKey=null)
|
|
{
|
|
$this->_apiKey = $apiKey;
|
|
}
|
|
|
|
public static function apiUrl($url='')
|
|
{
|
|
$apiBase = Stripe::$apiBase;
|
|
return "$apiBase$url";
|
|
}
|
|
|
|
public static function utf8($value)
|
|
{
|
|
if (is_string($value))
|
|
return utf8_encode($value);
|
|
else
|
|
return $value;
|
|
}
|
|
|
|
private static function _encodeObjects($d)
|
|
{
|
|
if ($d instanceof Stripe_ApiRequestor) {
|
|
return $d->id;
|
|
} else if ($d === true) {
|
|
return 'true';
|
|
} else if ($d === false) {
|
|
return 'false';
|
|
} else if (is_array($d)) {
|
|
$res = array();
|
|
foreach ($d as $k => $v)
|
|
$res[$k] = self::_encodeObjects($v);
|
|
return $res;
|
|
} else {
|
|
return $d;
|
|
}
|
|
}
|
|
|
|
public static function encode($d)
|
|
{
|
|
return http_build_query($d, null, '&');
|
|
}
|
|
|
|
public function request($meth, $url, $params=null)
|
|
{
|
|
if (!$params)
|
|
$params = array();
|
|
list($rbody, $rcode, $myApiKey) = $this->_requestRaw($meth, $url, $params);
|
|
$resp = $this->_interpretResponse($rbody, $rcode);
|
|
return array($resp, $myApiKey);
|
|
}
|
|
|
|
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);
|
|
$error = $resp['error'];
|
|
switch ($rcode) {
|
|
case 400:
|
|
case 404:
|
|
throw new Stripe_InvalidRequestError(isset($error['message']) ? $error['message'] : null,
|
|
isset($error['param']) ? $error['param'] : null,
|
|
$rcode, $rbody, $resp);
|
|
case 401:
|
|
throw new Stripe_AuthenticationError(isset($error['message']) ? $error['message'] : null, $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);
|
|
default:
|
|
throw new Stripe_ApiError(isset($error['message']) ? $error['message'] : null, $rcode, $rbody, $resp);
|
|
}
|
|
}
|
|
|
|
private function _requestRaw($meth, $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.');
|
|
|
|
$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);
|
|
$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);
|
|
return array($rbody, $rcode, $myApiKey);
|
|
}
|
|
|
|
private function _interpretResponse($rbody, $rcode)
|
|
{
|
|
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);
|
|
}
|
|
|
|
if ($rcode < 200 || $rcode >= 300) {
|
|
$this->handleApiError($rbody, $rcode, $resp);
|
|
}
|
|
return $resp;
|
|
}
|
|
|
|
private function _curlRequest($meth, $absUrl, $headers, $params, $myApiKey)
|
|
{
|
|
$curl = curl_init();
|
|
$meth = strtolower($meth);
|
|
$opts = array();
|
|
if ($meth == 'get') {
|
|
$opts[CURLOPT_HTTPGET] = 1;
|
|
if (count($params) > 0) {
|
|
$encoded = self::encode($params);
|
|
$absUrl = "$absUrl?$encoded";
|
|
}
|
|
} else if ($meth == 'post') {
|
|
$opts[CURLOPT_POST] = 1;
|
|
$opts[CURLOPT_POSTFIELDS] = self::encode($params);
|
|
} else if ($meth == 'delete') {
|
|
$opts[CURLOPT_CUSTOMREQUEST] = 'DELETE';
|
|
if (count($params) > 0) {
|
|
$encoded = self::encode($params);
|
|
$absUrl = "$absUrl?$encoded";
|
|
}
|
|
} else {
|
|
throw new Stripe_ApiError("Unrecognized method $meth");
|
|
}
|
|
|
|
$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;
|
|
$opts[CURLOPT_USERPWD] = $myApiKey . ':';
|
|
if (!Stripe::$verifySslCerts)
|
|
$opts[CURLOPT_SSL_VERIFYPEER] = false;
|
|
|
|
curl_setopt_array($curl, $opts);
|
|
$rbody = curl_exec($curl);
|
|
|
|
$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"}');
|
|
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
|
|
curl_setopt($curl, CURLOPT_CAINFO,
|
|
dirname(__FILE__) . '/../data/ca-certificates.crt');
|
|
$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);
|
|
}
|
|
|
|
public function handleCurlError($errno, $message)
|
|
{
|
|
$apiBase = Stripe::$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 let us know at support@stripe.com.";
|
|
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;
|
|
default:
|
|
$msg = "Unexpected error communicating with Stripe. If this problem persists, let us know at support@stripe.com.";
|
|
}
|
|
|
|
$msg .= "\n\n(Network error: $message)";
|
|
throw new Stripe_ApiConnectionError($msg);
|
|
}
|
|
}
|