mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 16:22:43 +01:00
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
This commit is contained in:
parent
877cb136e8
commit
cc586b0afa
42 changed files with 5710 additions and 8 deletions
17
externals/stripe-js/stripe_core.js
vendored
Normal file
17
externals/stripe-js/stripe_core.js
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* @provides stripe-core
|
||||
* @do-not-minify
|
||||
*/
|
||||
(function(c){function k(a){return a.replace(/^\s+|\s+$/g,"")}function n(){if(!c.publishableKey)throw"No Publishable API Key: Call Stripe.setPublishableKey to provide your key.";}var d=null,l={};typeof window!=="undefined"&&!window.JSON&&(window.JSON={});(function(){if(typeof JSON.parse!=="function")JSON.parse=function(a,b){function d(a,e){var c,h,f=a[e];if(f&&typeof f==="object")for(c in f)Object.hasOwnProperty.call(f,c)&&(h=d(f,c),h!==void 0?f[c]=h:delete f[c]);return b.call(a,e,f)}var e=RegExp("[\\u0000\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]",
|
||||
"g"),a=String(a);e.lastIndex=0;e.test(a)&&(a=a.replace(e,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return e=eval("("+a+")"),typeof b==="function"?d({"":e},""):e;throw new SyntaxError("JSON.parse");}})();var v=function(a){function b(){d=null;var a=document.getElementsByTagName("body")[0],
|
||||
b=document.createElement("iframe");m="stripeFrame"+(new Date).getTime();q=j.apiURL+"/js/v1/apitunnel.html";var i=q+"#"+encodeURIComponent(window.location.href);b.setAttribute("src",i);b.setAttribute("name",m);b.setAttribute("id",m);b.setAttribute("frameborder","0");b.setAttribute("scrolling","no");b.setAttribute("allowtransparency","true");b.setAttribute("width",0);b.setAttribute("height",0);b.setAttribute("style","position:absolute;top:0;left:0;width:0;height:0");i=function(){d=window.frames[m];
|
||||
c()};b.attachEvent?b.attachEvent("onload",i):b.onload=i;a.appendChild(b)}function c(){if(d){var b=o.length;if(b>0){for(var e=0;e<b;++e){var i=o[e].message,r=i.id;h[r]=o[e].callback;a.postMessage(i,j.apiURL,q,d);f[r]=window.setTimeout(function(a){h[a](504,{error:{message:"There was an error processing your card"}});delete h[a];delete f[a]},6E4,r)}o=[]}}}if(typeof a==="undefined"){var a={},e=function(a){if(typeof a==="undefined"){var a={},b=function(){var a={};a.serialize=function(b,e){var d=[],c;for(c in b)if(b.hasOwnProperty(c)){var u=
|
||||
e?e+"["+c+"]":c,f=b[c];d.push(typeof f=="object"?a.serialize(f,u):encodeURIComponent(u)+"="+encodeURIComponent(f))}return d.join("&")};a.deserialize=function(a){for(var b={},a=a.split("&"),e=a.length,c=null,d=null,i=0;i<e;++i){d=a[i].split("=");d[0]=decodeURIComponent(d[0]);d[1]=decodeURIComponent(d[1]);for(var f=d[0],c=[],g=-1;(g=f.indexOf("["))!==-1;)c.push(f.substr(g,f.indexOf("]")-g+1)),f=f.substr(f.indexOf("]")+1);if(c.length===0)b[d[0]]=d[1];else{g=d[0].substr(0,d[0].indexOf("["));typeof b[g]===
|
||||
"undefined"&&(b[g]={});for(var f=b[g],t=c.length,p=0;p<t-1;++p)g=c[p].substr(1,c[p].length-2),typeof f[g]==="undefined"&&(f[g]={}),f=f[g];c=c[t-1];g=c.substr(1,c.length-2);f[g]=d[1]}}return b};return a};typeof a!=="undefined"?a=b():exports.createSerializer=b}return{postMessage:function(b,c,d,e){if(typeof window!=="undefined")b=a.serialize(b),typeof window.postMessage==="undefined"?e.location.href=d+"#"+ +new Date+Math.floor(Math.random()*1E3)+"&"+b:e.postMessage(b,c)},receiveMessage:function(b,c){if(typeof window!==
|
||||
"undefined")if(window.postMessage)attachedCallback=function(d){if(d.origin.toLowerCase()!==c.toLowerCase())return!1;b(a.deserialize(d.data))},window.addEventListener?window.addEventListener("message",attachedCallback,!1):window.attachEvent("onmessage",attachedCallback);else{var d=window.location.hash;setInterval(function(){var c=window.location.hash,e=/^#?\d+&/;if(c!==d&&e.test(c))d=c,window.location.hash="",b(a.deserialize(c.replace(e,"")))},100)}}}};typeof a!=="undefined"?a=e():exports.createXD=
|
||||
e}var d=null,o=[],l=0,h={},f={},k=!1,m,q,j={apiURL:"https://api.stripe.com",onMessage:function(a){var b=a.id,c=null,c=a.response===null||a.response===""?{error:{message:"There was an error processing your card"}}:JSON.parse(a.response);h[b](parseInt(a.status),c);window.clearTimeout(f[b]);delete h[b];delete f[b]}},n=!1,s=function(){b();n||(a.receiveMessage(j.onMessage,j.apiURL),n=!0)};j.init=function(){if(!m||!document.getElementById(m))typeof document!=="undefined"&&document&&document.body?s():typeof window!==
|
||||
"undefined"&&window&&!k&&(window.addEventListener?window.addEventListener("load",s,!1):window.attachEvent&&window.attachEvent("onload",s)),k=!0};j.callAPI=function(a,b,d,e,f){if(a!=="POST"&&a!=="GET"&&a!=="DELETE")throw"You can only call the API with POST, GET or DELETE";j.init();var h=(l++).toString();o.push({message:{id:h,method:a,url:"/v1/"+b,params:d,key:e},callback:f});c()};return j};typeof l!=="undefined"?l=v():exports.createTransport=v;c.transport=l;c.validateCardNumber=function(a){var a=a.replace(/\s+|-/g,
|
||||
""),b;if(b=a.length>=10)if(b=a.length<=16)if(a.match(/^[0-9]+$/)===null)b=!1;else{var a=a.split("").reverse().join(""),c=0,d;for(b=0;b<a.length;++b)d=parseInt(a.charAt(b),10),b%2!=0&&(d*=2),c+=d<10?d:d-9;b=c!=0&&c%10==0}return b};c.cardType=function(a){if(!d){d={};for(var b=40;b<=49;++b)d[b]="Visa";for(b=50;b<=59;++b)d[b]="MasterCard";d[34]=d[37]="American Express";d[60]=d[62]=d[64]=d[65]="Discover";d[35]="JCB";d[30]=d[36]=d[38]=d[39]="Diners Club"}a=d[a.substr(0,2)];return typeof a==="undefined"?
|
||||
"Unknown":a};c.validateCVC=function(a){a=k(a);return a.match(/^[0-9]+$/)!==null&&a.length>=3&&a.length<=4};c.validateExpiry=function(a,b){var a=k(a),b=k(b),c=new Date;return a.match(/^[0-9]+$/)!==null&&b.match(/^[0-9]+$/)!==null&&b>c.getFullYear()||b==c.getFullYear()&&a>=c.getMonth()+1};c.createToken=function(a,b,d){typeof b==="function"&&(d=b,b=null);n();var e={expMonth:"exp_month",expYear:"exp_year",addressLine1:"address_line_1",addressLine2:"address_line_2",addressZip:"address_zip",addressState:"address_state",
|
||||
addressCountry:"address_country"};for(convertibleParam in e)e.hasOwnProperty(convertibleParam)&&a.hasOwnProperty(convertibleParam)&&(a[e[convertibleParam]]=a[convertibleParam],delete a[convertibleParam]);params={card:a};b!==null&&(params.amount=b);c.transport.callAPI("POST","tokens",params,c.publishableKey,d)};c.getToken=function(a,b){n();c.transport.callAPI("GET","tokens/"+a,{},c.publishableKey,b)};c.setPublishableKey=function(a){c.publishableKey=a};l.init()})(typeof exports!=="undefined"&&exports!==
|
||||
null?exports:window.Stripe={});
|
21
externals/stripe-php/LICENSE
vendored
Normal file
21
externals/stripe-php/LICENSE
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2010 Stripe
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
20
externals/stripe-php/README.rdoc
vendored
Normal file
20
externals/stripe-php/README.rdoc
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
= Installation
|
||||
|
||||
Obtain the latest version of the Stripe PHP bindings with:
|
||||
|
||||
git clone https://github.com/stripe/stripe-php
|
||||
|
||||
To get started, add the following to your PHP script:
|
||||
|
||||
require_once("/path/to/stripe-php/lib/Stripe.php");
|
||||
|
||||
Simple usage looks like:
|
||||
|
||||
Stripe::setApiKey('d8e8fca2dc0f896fd7cb4cb0031ba249');
|
||||
$myCard = array('number' => '4242424242424242', 'exp_month' => 5, 'exp_year' => 2015);
|
||||
$charge = Stripe_Charge::create(array('card' => $myCard, 'amount' => 2000, 'currency' => 'usd'));
|
||||
echo $charge;
|
||||
|
||||
= Documentation
|
||||
|
||||
Please see https://stripe.com/api for up-to-date documentation.
|
1
externals/stripe-php/VERSION
vendored
Normal file
1
externals/stripe-php/VERSION
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
1.6.3
|
66
externals/stripe-php/lib/Stripe.php
vendored
Normal file
66
externals/stripe-php/lib/Stripe.php
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
// Tested on PHP 5.2, 5.3
|
||||
|
||||
// This snippet (and some of the curl code) due to the Facebook SDK.
|
||||
if (!function_exists('curl_init')) {
|
||||
throw new Exception('Stripe needs the CURL PHP extension.');
|
||||
}
|
||||
if (!function_exists('json_decode')) {
|
||||
throw new Exception('Stripe needs the JSON PHP extension.');
|
||||
}
|
||||
|
||||
|
||||
abstract class Stripe
|
||||
{
|
||||
public static $apiKey;
|
||||
public static $apiBase = 'https://api.stripe.com/v1';
|
||||
public static $verifySslCerts = true;
|
||||
const VERSION = '1.6.3';
|
||||
|
||||
public static function getApiKey()
|
||||
{
|
||||
return self::$apiKey;
|
||||
}
|
||||
|
||||
public static function setApiKey($apiKey)
|
||||
{
|
||||
self::$apiKey = $apiKey;
|
||||
}
|
||||
|
||||
public static function getVerifySslCerts() {
|
||||
return self::$verifySslCerts;
|
||||
}
|
||||
|
||||
public static function setVerifySslCerts($verify) {
|
||||
self::$verifySslCerts = $verify;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Utilities
|
||||
require(dirname(__FILE__) . '/Stripe/Util.php');
|
||||
require(dirname(__FILE__) . '/Stripe/Util/Set.php');
|
||||
|
||||
// Errors
|
||||
require(dirname(__FILE__) . '/Stripe/Error.php');
|
||||
require(dirname(__FILE__) . '/Stripe/ApiError.php');
|
||||
require(dirname(__FILE__) . '/Stripe/ApiConnectionError.php');
|
||||
require(dirname(__FILE__) . '/Stripe/AuthenticationError.php');
|
||||
require(dirname(__FILE__) . '/Stripe/CardError.php');
|
||||
require(dirname(__FILE__) . '/Stripe/InvalidRequestError.php');
|
||||
|
||||
// Plumbing
|
||||
require(dirname(__FILE__) . '/Stripe/Object.php');
|
||||
require(dirname(__FILE__) . '/Stripe/ApiRequestor.php');
|
||||
require(dirname(__FILE__) . '/Stripe/ApiResource.php');
|
||||
|
||||
// Stripe API Resources
|
||||
require(dirname(__FILE__) . '/Stripe/Charge.php');
|
||||
require(dirname(__FILE__) . '/Stripe/Customer.php');
|
||||
require(dirname(__FILE__) . '/Stripe/Invoice.php');
|
||||
require(dirname(__FILE__) . '/Stripe/InvoiceItem.php');
|
||||
require(dirname(__FILE__) . '/Stripe/Plan.php');
|
||||
require(dirname(__FILE__) . '/Stripe/Token.php');
|
||||
require(dirname(__FILE__) . '/Stripe/Coupon.php');
|
||||
require(dirname(__FILE__) . '/Stripe/Event.php');
|
5
externals/stripe-php/lib/Stripe/ApiConnectionError.php
vendored
Normal file
5
externals/stripe-php/lib/Stripe/ApiConnectionError.php
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
class Stripe_ApiConnectionError extends Stripe_Error
|
||||
{
|
||||
}
|
5
externals/stripe-php/lib/Stripe/ApiError.php
vendored
Normal file
5
externals/stripe-php/lib/Stripe/ApiError.php
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
class Stripe_ApiError extends Stripe_Error
|
||||
{
|
||||
}
|
197
externals/stripe-php/lib/Stripe/ApiRequestor.php
vendored
Normal file
197
externals/stripe-php/lib/Stripe/ApiRequestor.php
vendored
Normal file
|
@ -0,0 +1,197 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
97
externals/stripe-php/lib/Stripe/ApiResource.php
vendored
Normal file
97
externals/stripe-php/lib/Stripe/ApiResource.php
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
<?php
|
||||
|
||||
abstract class Stripe_ApiResource extends Stripe_Object
|
||||
{
|
||||
protected static function _scopedRetrieve($class, $id, $apiKey=null)
|
||||
{
|
||||
$instance = new $class($id, $apiKey);
|
||||
$instance->refresh();
|
||||
return $instance;
|
||||
}
|
||||
|
||||
public function refresh()
|
||||
{
|
||||
$requestor = new Stripe_ApiRequestor($this->_apiKey);
|
||||
$url = $this->instanceUrl();
|
||||
list($response, $apiKey) = $requestor->request('get', $url);
|
||||
$this->refreshFrom($response, $apiKey);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public static function classUrl($class)
|
||||
{
|
||||
// Useful for namespaces: Foo\Stripe_Charge
|
||||
if ($postfix = strrchr($class, '\\'))
|
||||
$class = substr($postfix, 1);
|
||||
if (substr($class, 0, strlen('Stripe')) == 'Stripe')
|
||||
$class = substr($class, strlen('Stripe'));
|
||||
$class = str_replace('_', '', $class);
|
||||
$name = urlencode($class);
|
||||
$name = strtolower($name);
|
||||
return "/${name}s";
|
||||
}
|
||||
|
||||
public function instanceUrl()
|
||||
{
|
||||
$id = $this['id'];
|
||||
$class = get_class($this);
|
||||
if (!$id) {
|
||||
throw new Stripe_InvalidRequestError("Could not determine which URL to request: $class instance has invalid ID: $id");
|
||||
}
|
||||
$id = Stripe_ApiRequestor::utf8($id);
|
||||
$base = self::classUrl($class);
|
||||
$extn = urlencode($id);
|
||||
return "$base/$extn";
|
||||
}
|
||||
|
||||
private static function _validateCall($method, $params=null, $apiKey=null)
|
||||
{
|
||||
if ($params && !is_array($params))
|
||||
throw new Stripe_Error("You must pass an array as the first argument to Stripe API method calls. (HINT: an example call to create a charge would be: \"StripeCharge::create(array('amount' => 100, 'currency' => 'usd', 'card' => array('number' => 4242424242424242, 'exp_month' => 5, 'exp_year' => 2015)))\")");
|
||||
if ($apiKey && !is_string($apiKey))
|
||||
throw new Stripe_Error('The second argument to Stripe API method calls is an optional per-request apiKey, which must be a string. (HINT: you can set a global apiKey by "Stripe::setApiKey(<apiKey>)")');
|
||||
}
|
||||
|
||||
protected static function _scopedAll($class, $params=null, $apiKey=null)
|
||||
{
|
||||
self::_validateCall('all', $params, $apiKey);
|
||||
$requestor = new Stripe_ApiRequestor($apiKey);
|
||||
$url = self::classUrl($class);
|
||||
list($response, $apiKey) = $requestor->request('get', $url, $params);
|
||||
return Stripe_Util::convertToStripeObject($response, $apiKey);
|
||||
}
|
||||
|
||||
protected static function _scopedCreate($class, $params=null, $apiKey=null)
|
||||
{
|
||||
self::_validateCall('create', $params, $apiKey);
|
||||
$requestor = new Stripe_ApiRequestor($apiKey);
|
||||
$url = self::classUrl($class);
|
||||
list($response, $apiKey) = $requestor->request('post', $url, $params);
|
||||
return Stripe_Util::convertToStripeObject($response, $apiKey);
|
||||
}
|
||||
|
||||
protected function _scopedSave($class)
|
||||
{
|
||||
self::_validateCall('save');
|
||||
if ($this->_unsavedValues) {
|
||||
$requestor = new Stripe_ApiRequestor($this->_apiKey);
|
||||
$params = array();
|
||||
foreach ($this->_unsavedValues->toArray() as $k)
|
||||
$params[$k] = $this->$k;
|
||||
$url = $this->instanceUrl();
|
||||
list($response, $apiKey) = $requestor->request('post', $url, $params);
|
||||
$this->refreshFrom($response, $apiKey);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function _scopedDelete($class, $params=null)
|
||||
{
|
||||
self::_validateCall('delete');
|
||||
$requestor = new Stripe_ApiRequestor($this->_apiKey);
|
||||
$url = $this->instanceUrl();
|
||||
list($response, $apiKey) = $requestor->request('delete', $url, $params);
|
||||
$this->refreshFrom($response, $apiKey);
|
||||
return $this;
|
||||
}
|
||||
}
|
5
externals/stripe-php/lib/Stripe/AuthenticationError.php
vendored
Normal file
5
externals/stripe-php/lib/Stripe/AuthenticationError.php
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
class Stripe_AuthenticationError extends Stripe_Error
|
||||
{
|
||||
}
|
11
externals/stripe-php/lib/Stripe/CardError.php
vendored
Normal file
11
externals/stripe-php/lib/Stripe/CardError.php
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
class Stripe_CardError extends Stripe_Error
|
||||
{
|
||||
public function __construct($message, $param, $code, $http_status=null, $http_body=null, $json_body=null)
|
||||
{
|
||||
parent::__construct($message, $http_status, $http_body, $json_body);
|
||||
$this->param = $param;
|
||||
$this->code = $code;
|
||||
}
|
||||
}
|
46
externals/stripe-php/lib/Stripe/Charge.php
vendored
Normal file
46
externals/stripe-php/lib/Stripe/Charge.php
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
class Stripe_Charge extends Stripe_ApiResource
|
||||
{
|
||||
public static function constructFrom($values, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::scopedConstructFrom($class, $values, $apiKey);
|
||||
}
|
||||
|
||||
public static function retrieve($id, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedRetrieve($class, $id, $apiKey);
|
||||
}
|
||||
|
||||
public static function all($params=null, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedAll($class, $params, $apiKey);
|
||||
}
|
||||
|
||||
public static function create($params=null, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedCreate($class, $params, $apiKey);
|
||||
}
|
||||
|
||||
public function refund($params=null)
|
||||
{
|
||||
$requestor = new Stripe_ApiRequestor($this->_apiKey);
|
||||
$url = $this->instanceUrl() . '/refund';
|
||||
list($response, $apiKey) = $requestor->request('post', $url, $params);
|
||||
$this->refreshFrom($response, $apiKey);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function capture($params=null)
|
||||
{
|
||||
$requestor = new Stripe_ApiRequestor($this->_apiKey);
|
||||
$url = $this->instanceUrl() . '/capture';
|
||||
list($response, $apiKey) = $requestor->request('post', $url, $params);
|
||||
$this->refreshFrom($response, $apiKey);
|
||||
return $this;
|
||||
}
|
||||
}
|
34
externals/stripe-php/lib/Stripe/Coupon.php
vendored
Normal file
34
externals/stripe-php/lib/Stripe/Coupon.php
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
class Stripe_Coupon extends Stripe_ApiResource
|
||||
{
|
||||
public static function constructFrom($values, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::scopedConstructFrom($class, $values, $apiKey);
|
||||
}
|
||||
|
||||
public static function retrieve($id, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedRetrieve($class, $id, $apiKey);
|
||||
}
|
||||
|
||||
public static function create($params=null, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedCreate($class, $params, $apiKey);
|
||||
}
|
||||
|
||||
public function delete($params=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedDelete($class, $params);
|
||||
}
|
||||
|
||||
public static function all($params=null, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedAll($class, $params, $apiKey);
|
||||
}
|
||||
}
|
94
externals/stripe-php/lib/Stripe/Customer.php
vendored
Normal file
94
externals/stripe-php/lib/Stripe/Customer.php
vendored
Normal file
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
class Stripe_Customer extends Stripe_ApiResource
|
||||
{
|
||||
public static function constructFrom($values, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::scopedConstructFrom($class, $values, $apiKey);
|
||||
}
|
||||
|
||||
public static function retrieve($id, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedRetrieve($class, $id, $apiKey);
|
||||
}
|
||||
|
||||
public static function all($params=null, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedAll($class, $params, $apiKey);
|
||||
}
|
||||
|
||||
public static function create($params=null, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedCreate($class, $params, $apiKey);
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedSave($class);
|
||||
}
|
||||
|
||||
public function delete($params=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedDelete($class, $params);
|
||||
}
|
||||
|
||||
public function addInvoiceItem($params=null)
|
||||
{
|
||||
if (!$params)
|
||||
$params = array();
|
||||
$params['customer'] = $this->id;
|
||||
$ii = Stripe_InvoiceItem::create($params, $this->_apiKey);
|
||||
return $ii;
|
||||
}
|
||||
|
||||
public function invoices($params=null)
|
||||
{
|
||||
if (!$params)
|
||||
$params = array();
|
||||
$params['customer'] = $this->id;
|
||||
$invoices = Stripe_Invoice::all($params, $this->_apiKey);
|
||||
return $invoices;
|
||||
}
|
||||
|
||||
public function invoiceItems($params=null)
|
||||
{
|
||||
if (!$params)
|
||||
$params = array();
|
||||
$params['customer'] = $this->id;
|
||||
$iis = Stripe_InvoiceItem::all($params, $this->_apiKey);
|
||||
return $iis;
|
||||
}
|
||||
|
||||
public function charges($params=null)
|
||||
{
|
||||
if (!$params)
|
||||
$params = array();
|
||||
$params['customer'] = $this->id;
|
||||
$charges = Stripe_Charge::all($params, $this->_apiKey);
|
||||
return $charges;
|
||||
}
|
||||
|
||||
public function updateSubscription($params=null)
|
||||
{
|
||||
$requestor = new Stripe_ApiRequestor($this->_apiKey);
|
||||
$url = $this->instanceUrl() . '/subscription';
|
||||
list($response, $apiKey) = $requestor->request('post', $url, $params);
|
||||
$this->refreshFrom(array('subscription' => $response), $apiKey, true);
|
||||
return $this->subscription;
|
||||
}
|
||||
|
||||
public function cancelSubscription($params=null)
|
||||
{
|
||||
$requestor = new Stripe_ApiRequestor($this->_apiKey);
|
||||
$url = $this->instanceUrl() . '/subscription';
|
||||
list($response, $apiKey) = $requestor->request('delete', $url, $params);
|
||||
$this->refreshFrom(array('subscription' => $response), $apiKey, true);
|
||||
return $this->subscription;
|
||||
}
|
||||
}
|
27
externals/stripe-php/lib/Stripe/Error.php
vendored
Normal file
27
externals/stripe-php/lib/Stripe/Error.php
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
class Stripe_Error extends Exception
|
||||
{
|
||||
public function __construct($message=null, $http_status=null, $http_body=null, $json_body=null)
|
||||
{
|
||||
parent::__construct($message);
|
||||
$this->http_status = $http_status;
|
||||
$this->http_body = $http_body;
|
||||
$this->json_body = $json_body;
|
||||
}
|
||||
|
||||
public function getHttpStatus()
|
||||
{
|
||||
return $this->http_status;
|
||||
}
|
||||
|
||||
public function getHttpBody()
|
||||
{
|
||||
return $this->http_body;
|
||||
}
|
||||
|
||||
public function getJsonBody()
|
||||
{
|
||||
return $this->json_body;
|
||||
}
|
||||
}
|
22
externals/stripe-php/lib/Stripe/Event.php
vendored
Normal file
22
externals/stripe-php/lib/Stripe/Event.php
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
class Stripe_Event extends Stripe_ApiResource
|
||||
{
|
||||
public static function constructFrom($values, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::scopedConstructFrom($class, $values, $apiKey);
|
||||
}
|
||||
|
||||
public static function retrieve($id, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedRetrieve($class, $id, $apiKey);
|
||||
}
|
||||
|
||||
public static function all($params=null, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedAll($class, $params, $apiKey);
|
||||
}
|
||||
}
|
10
externals/stripe-php/lib/Stripe/InvalidRequestError.php
vendored
Normal file
10
externals/stripe-php/lib/Stripe/InvalidRequestError.php
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
class Stripe_InvalidRequestError extends Stripe_Error
|
||||
{
|
||||
public function __construct($message, $param, $http_status=null, $http_body=null, $json_body=null)
|
||||
{
|
||||
parent::__construct($message, $http_status, $http_body, $json_body);
|
||||
$this->param = $param;
|
||||
}
|
||||
}
|
30
externals/stripe-php/lib/Stripe/Invoice.php
vendored
Normal file
30
externals/stripe-php/lib/Stripe/Invoice.php
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
class Stripe_Invoice extends Stripe_ApiResource
|
||||
{
|
||||
public static function constructFrom($values, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::scopedConstructFrom($class, $values, $apiKey);
|
||||
}
|
||||
|
||||
public static function retrieve($id, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedRetrieve($class, $id, $apiKey);
|
||||
}
|
||||
|
||||
public static function all($params=null, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedAll($class, $params, $apiKey);
|
||||
}
|
||||
|
||||
public static function upcoming($params=null, $apiKey=null)
|
||||
{
|
||||
$requestor = new Stripe_ApiRequestor($apiKey);
|
||||
$url = self::classUrl(get_class()) . '/upcoming';
|
||||
list($response, $apiKey) = $requestor->request('get', $url, $params);
|
||||
return Stripe_Util::convertToStripeObject($response, $apiKey);
|
||||
}
|
||||
}
|
40
externals/stripe-php/lib/Stripe/InvoiceItem.php
vendored
Normal file
40
externals/stripe-php/lib/Stripe/InvoiceItem.php
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
class Stripe_InvoiceItem extends Stripe_ApiResource
|
||||
{
|
||||
public static function constructFrom($values, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::scopedConstructFrom($class, $values, $apiKey);
|
||||
}
|
||||
|
||||
public static function retrieve($id, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedRetrieve($class, $id, $apiKey);
|
||||
}
|
||||
|
||||
public static function all($params=null, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedAll($class, $params, $apiKey);
|
||||
}
|
||||
|
||||
public static function create($params=null, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedCreate($class, $params, $apiKey);
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedSave($class);
|
||||
}
|
||||
|
||||
public function delete($params=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedDelete($class, $params);
|
||||
}
|
||||
}
|
142
externals/stripe-php/lib/Stripe/Object.php
vendored
Normal file
142
externals/stripe-php/lib/Stripe/Object.php
vendored
Normal file
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
|
||||
class Stripe_Object implements ArrayAccess
|
||||
{
|
||||
public static $_permanentAttributes;
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$_permanentAttributes = new Stripe_Util_Set(array('_apiKey'));
|
||||
}
|
||||
|
||||
protected $_apiKey;
|
||||
protected $_values;
|
||||
protected $_unsavedValues;
|
||||
protected $_transientValues;
|
||||
|
||||
public function __construct($id=null, $apiKey=null)
|
||||
{
|
||||
$this->_apiKey = $apiKey;
|
||||
$this->_values = array();
|
||||
$this->_unsavedValues = new Stripe_Util_Set();
|
||||
$this->_transientValues = new Stripe_Util_Set();
|
||||
if ($id)
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
// Standard accessor magic methods
|
||||
public function __set($k, $v)
|
||||
{
|
||||
// TODO: may want to clear from $_transientValues. (Won't be user-visible.)
|
||||
$this->_values[$k] = $v;
|
||||
if (!self::$_permanentAttributes->includes($k))
|
||||
$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 (isset($this->_values[$k])) {
|
||||
return $this->_values[$k];
|
||||
} else if ($this->_transientValues->includes($k)) {
|
||||
$class = get_class($this);
|
||||
$attrs = join(', ', array_keys($this->_values));
|
||||
error_log("Stripe Notice: Undefined property of $class instance: $k. HINT: The $k attribute was set in the past, however. It was then wiped when refreshing the object with the result returned by Stripe's API, probably as a result of a save(). The attributes currently available on this object are: $attrs");
|
||||
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 isset($this->$k);
|
||||
}
|
||||
public function offsetUnset($k)
|
||||
{
|
||||
unset($this->$k);
|
||||
}
|
||||
public function offsetGet($k)
|
||||
{
|
||||
return isset($this->_values[$k]) ? $this->_values[$k] : null;
|
||||
}
|
||||
|
||||
// This unfortunately needs to be public to be used in Util.php
|
||||
public static function scopedConstructFrom($class, $values, $apiKey=null)
|
||||
{
|
||||
$obj = new $class(isset($values['id']) ? $values['id'] : null, $apiKey);
|
||||
$obj->refreshFrom($values, $apiKey);
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public static function constructFrom($values, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::scopedConstructFrom($class, $values, $apiKey);
|
||||
}
|
||||
|
||||
public function refreshFrom($values, $apiKey, $partial=false)
|
||||
{
|
||||
$this->_apiKey = $apiKey;
|
||||
// Wipe old state before setting new. This is useful for e.g. updating a
|
||||
// customer, where there is no persistent card parameter. Mark those values
|
||||
// which don't persist as transient
|
||||
if ($partial)
|
||||
$removed = new Stripe_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))
|
||||
continue;
|
||||
$this->_values[$k] = Stripe_Util::convertToStripeObject($v, $apiKey);
|
||||
$this->_transientValues->discard($k);
|
||||
$this->_unsavedValues->discard($k);
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
return $this->__toJSON();
|
||||
}
|
||||
|
||||
public function __toArray($recursive=false)
|
||||
{
|
||||
if ($recursive)
|
||||
return Stripe_Util::convertStripeObjectToArray($this->_values);
|
||||
else
|
||||
return $this->_values;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Stripe_Object::init();
|
40
externals/stripe-php/lib/Stripe/Plan.php
vendored
Normal file
40
externals/stripe-php/lib/Stripe/Plan.php
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
class Stripe_Plan extends Stripe_ApiResource
|
||||
{
|
||||
public static function constructFrom($values, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::scopedConstructFrom($class, $values, $apiKey);
|
||||
}
|
||||
|
||||
public static function retrieve($id, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedRetrieve($class, $id, $apiKey);
|
||||
}
|
||||
|
||||
public static function create($params=null, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedCreate($class, $params, $apiKey);
|
||||
}
|
||||
|
||||
public function delete($params=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedDelete($class, $params);
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedSave($class);
|
||||
}
|
||||
|
||||
public static function all($params=null, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedAll($class, $params, $apiKey);
|
||||
}
|
||||
}
|
22
externals/stripe-php/lib/Stripe/Token.php
vendored
Normal file
22
externals/stripe-php/lib/Stripe/Token.php
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
class Stripe_Token extends Stripe_ApiResource
|
||||
{
|
||||
public static function constructFrom($values, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::scopedConstructFrom($class, $values, $apiKey);
|
||||
}
|
||||
|
||||
public static function retrieve($id, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedRetrieve($class, $id, $apiKey);
|
||||
}
|
||||
|
||||
public static function create($params=null, $apiKey=null)
|
||||
{
|
||||
$class = get_class();
|
||||
return self::_scopedCreate($class, $params, $apiKey);
|
||||
}
|
||||
}
|
59
externals/stripe-php/lib/Stripe/Util.php
vendored
Normal file
59
externals/stripe-php/lib/Stripe/Util.php
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
abstract class Stripe_Util
|
||||
{
|
||||
public static function isList($array)
|
||||
{
|
||||
if (!is_array($array))
|
||||
return false;
|
||||
// TODO: this isn't actually correct in general, but it's correct given Stripe's responses
|
||||
foreach (array_keys($array) as $k) {
|
||||
if (!is_numeric($k))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function convertStripeObjectToArray($values)
|
||||
{
|
||||
$results = array();
|
||||
foreach ($values as $k => $v) {
|
||||
// FIXME: this is an encapsulation violation
|
||||
if (Stripe_Object::$_permanentAttributes->includes($k)) {
|
||||
continue;
|
||||
}
|
||||
if ($v instanceof Stripe_Object) {
|
||||
$results[$k] = $v->__toArray(true);
|
||||
}
|
||||
else if (is_array($v)) {
|
||||
$results[$k] = self::convertStripeObjectToArray($v);
|
||||
}
|
||||
else {
|
||||
$results[$k] = $v;
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
public static function convertToStripeObject($resp, $apiKey)
|
||||
{
|
||||
$types = array('charge' => 'Stripe_Charge',
|
||||
'customer' => 'Stripe_Customer',
|
||||
'invoice' => 'Stripe_Invoice',
|
||||
'invoiceitem' => 'Stripe_InvoiceItem', 'event' => 'Stripe_Event');
|
||||
if (self::isList($resp)) {
|
||||
$mapped = array();
|
||||
foreach ($resp as $i)
|
||||
array_push($mapped, self::convertToStripeObject($i, $apiKey));
|
||||
return $mapped;
|
||||
} else if (is_array($resp)) {
|
||||
if (isset($resp['object']) && is_string($resp['object']) && isset($types[$resp['object']]))
|
||||
$class = $types[$resp['object']];
|
||||
else
|
||||
$class = 'Stripe_Object';
|
||||
return Stripe_Object::scopedConstructFrom($class, $resp, $apiKey);
|
||||
} else {
|
||||
return $resp;
|
||||
}
|
||||
}
|
||||
}
|
34
externals/stripe-php/lib/Stripe/Util/Set.php
vendored
Normal file
34
externals/stripe-php/lib/Stripe/Util/Set.php
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
class Stripe_Util_Set
|
||||
{
|
||||
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]);
|
||||
}
|
||||
|
||||
// TODO: make Set support foreach
|
||||
public function toArray()
|
||||
{
|
||||
return array_keys($this->_elts);
|
||||
}
|
||||
}
|
3918
externals/stripe-php/lib/data/ca-certificates.crt
vendored
Normal file
3918
externals/stripe-php/lib/data/ca-certificates.crt
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -993,6 +993,19 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/js/application/repository/repository-crossreference.js',
|
||||
),
|
||||
'javelin-behavior-stripe-payment-form' =>
|
||||
array(
|
||||
'uri' => '/res/b77a4b16/rsrc/js/application/phortune/behavior-stripe-payment-form.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
2 => 'javelin-json',
|
||||
3 => 'stripe-core',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/phortune/behavior-stripe-payment-form.js',
|
||||
),
|
||||
'javelin-behavior-view-placeholder' =>
|
||||
array(
|
||||
'uri' => '/res/5b89bdf5/rsrc/js/javelin/ext/view/ViewPlaceholder.js',
|
||||
|
@ -2022,6 +2035,24 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/js/raphael/g.raphael.line.js',
|
||||
),
|
||||
'stripe-core' =>
|
||||
array(
|
||||
'uri' => '/res/3b0f0ad4/rsrc/js/stripe/stripe_core.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
),
|
||||
'disk' => '/rsrc/js/stripe/stripe_core.js',
|
||||
),
|
||||
'stripe-payment-form-css' =>
|
||||
array(
|
||||
'uri' => '/res/e2358ded/rsrc/css/application/phortune/stripe-payment-form.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
),
|
||||
'disk' => '/rsrc/css/application/phortune/stripe-payment-form.css',
|
||||
),
|
||||
'syntax-highlighting-css' =>
|
||||
array(
|
||||
'uri' => '/res/5669beb6/rsrc/css/core/syntax.css',
|
||||
|
|
|
@ -914,6 +914,10 @@ phutil_register_library_map(array(
|
|||
'PhabricatorXHProfProfileSymbolView' => 'applications/xhprof/view/symbol',
|
||||
'PhabricatorXHProfProfileTopLevelView' => 'applications/xhprof/view/toplevel',
|
||||
'PhabricatorXHProfProfileView' => 'applications/xhprof/view/base',
|
||||
'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/monthyearexpiry',
|
||||
'PhortuneStripeBaseController' => 'applications/phortune/stripe/controller/base',
|
||||
'PhortuneStripePaymentFormView' => 'applications/phortune/stripe/view/paymentform',
|
||||
'PhortuneStripeTestPaymentFormController' => 'applications/phortune/stripe/controller/testpaymentform',
|
||||
'PhrictionActionConstants' => 'applications/phriction/constants/action',
|
||||
'PhrictionChangeType' => 'applications/phriction/constants/changetype',
|
||||
'PhrictionConstants' => 'applications/phriction/constants/base',
|
||||
|
@ -1712,6 +1716,10 @@ phutil_register_library_map(array(
|
|||
'PhabricatorXHProfProfileSymbolView' => 'PhabricatorXHProfProfileView',
|
||||
'PhabricatorXHProfProfileTopLevelView' => 'PhabricatorXHProfProfileView',
|
||||
'PhabricatorXHProfProfileView' => 'AphrontView',
|
||||
'PhortuneMonthYearExpiryControl' => 'AphrontFormControl',
|
||||
'PhortuneStripeBaseController' => 'PhabricatorController',
|
||||
'PhortuneStripePaymentFormView' => 'AphrontView',
|
||||
'PhortuneStripeTestPaymentFormController' => 'PhortuneStripeBaseController',
|
||||
'PhrictionActionConstants' => 'PhrictionConstants',
|
||||
'PhrictionChangeType' => 'PhrictionConstants',
|
||||
'PhrictionContent' => 'PhrictionDAO',
|
||||
|
|
|
@ -412,6 +412,12 @@ class AphrontDefaultApplicationConfiguration
|
|||
'edit/(?P<phid>[^/]+)/' => 'PhabricatorFlagEditController',
|
||||
'delete/(?P<id>\d+)/' => 'PhabricatorFlagDeleteController',
|
||||
),
|
||||
|
||||
'/phortune/' => array(
|
||||
'stripe/' => array(
|
||||
'testpaymentform/' => 'PhortuneStripeTestPaymentFormController',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class PhortuneMonthYearExpiryControl extends AphrontFormControl {
|
||||
private $user;
|
||||
private $monthValue;
|
||||
private $yearValue;
|
||||
|
||||
public function setUser(PhabricatorUser $user) {
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
private function getUser() {
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setMonthInputValue($value) {
|
||||
$this->monthValue = $value;
|
||||
return $this;
|
||||
}
|
||||
private function getMonthInputValue() {
|
||||
return $this->monthValue;
|
||||
}
|
||||
private function getCurrentMonth() {
|
||||
return phabricator_format_local_time(
|
||||
time(),
|
||||
$this->getUser(),
|
||||
'm');
|
||||
}
|
||||
|
||||
public function setYearInputValue($value) {
|
||||
$this->yearValue = $value;
|
||||
return $this;
|
||||
}
|
||||
private function getYearInputValue() {
|
||||
return $this->yearValue;
|
||||
}
|
||||
private function getCurrentYear() {
|
||||
return phabricator_format_local_time(
|
||||
time(),
|
||||
$this->getUser(),
|
||||
'Y');
|
||||
}
|
||||
|
||||
protected function getCustomControlClass() {
|
||||
return 'aphront-form-control-text';
|
||||
}
|
||||
|
||||
protected function renderInput() {
|
||||
if (!$this->getUser()) {
|
||||
throw new Exception('You must setUser() before render()!');
|
||||
}
|
||||
|
||||
// represent months like a credit card does
|
||||
$months = array(
|
||||
'01' => '01',
|
||||
'02' => '02',
|
||||
'03' => '03',
|
||||
'04' => '04',
|
||||
'05' => '05',
|
||||
'06' => '06',
|
||||
'07' => '07',
|
||||
'08' => '08',
|
||||
'09' => '09',
|
||||
'10' => '10',
|
||||
'11' => '11',
|
||||
'12' => '12',
|
||||
);
|
||||
|
||||
$current_year = $this->getCurrentYear();
|
||||
$years = range($current_year, $current_year + 20);
|
||||
$years = array_combine($years, $years);
|
||||
|
||||
if ($this->getMonthInputValue()) {
|
||||
$selected_month = $this->getMonthInputValue();
|
||||
} else {
|
||||
$selected_month = $this->getCurrentMonth();
|
||||
}
|
||||
$months_sel = AphrontFormSelectControl::renderSelectTag(
|
||||
$selected_month,
|
||||
$months,
|
||||
array(
|
||||
'sigil' => 'month-input',
|
||||
));
|
||||
|
||||
$years_sel = AphrontFormSelectControl::renderSelectTag(
|
||||
$this->getYearInputValue(),
|
||||
$years,
|
||||
array(
|
||||
'sigil' => 'year-input',
|
||||
));
|
||||
|
||||
return self::renderSingleView(
|
||||
array(
|
||||
$months_sel,
|
||||
$years_sel
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'view/form/control/base');
|
||||
phutil_require_module('phabricator', 'view/form/control/select');
|
||||
phutil_require_module('phabricator', 'view/utils');
|
||||
|
||||
|
||||
phutil_require_source('PhortuneMonthYearExpiryControl.php');
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
abstract class PhortuneStripeBaseController extends PhabricatorController {
|
||||
|
||||
public function buildStandardPageResponse($view, array $data) {
|
||||
$page = $this->buildStandardPageView();
|
||||
|
||||
$page->setApplicationName('Phortune - Stripe');
|
||||
$page->setBaseURI('/phortune/stripe/');
|
||||
$page->setTitle(idx($data, 'title'));
|
||||
$page->appendChild($view);
|
||||
|
||||
$response = new AphrontWebpageResponse();
|
||||
return $response->setContent($page->render());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'aphront/response/webpage');
|
||||
phutil_require_module('phabricator', 'applications/base/controller/base');
|
||||
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
||||
phutil_require_source('PhortuneStripeBaseController.php');
|
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class PhortuneStripeTestPaymentFormController
|
||||
extends PhortuneStripeBaseController {
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
$title = 'Test Payment Form';
|
||||
$error_view = null;
|
||||
$card_number_error = null;
|
||||
$card_cvc_error = null;
|
||||
$card_expiration_error = null;
|
||||
$stripe_key = $request->getStr('stripeKey');
|
||||
if (!$stripe_key) {
|
||||
$error_view = id(new AphrontErrorView())
|
||||
->setTitle('Missing stripeKey parameter in URI');
|
||||
}
|
||||
|
||||
if (!$error_view && $request->isFormPost()) {
|
||||
$card_errors = $request->getStr('cardErrors');
|
||||
$stripe_token = $request->getStr('stripeToken');
|
||||
if ($card_errors) {
|
||||
$raw_errors = json_decode($card_errors);
|
||||
list($card_number_error,
|
||||
$card_cvc_error,
|
||||
$card_expiration_error,
|
||||
$messages) = $this->parseRawErrors($raw_errors);
|
||||
$error_view = id(new AphrontErrorView())
|
||||
->setTitle('There were errors processing your card.')
|
||||
->setErrors($messages);
|
||||
} else if (!$stripe_token) {
|
||||
// this shouldn't happen, so show the user a very generic error
|
||||
// message and log that this error occurred...!
|
||||
$error_view = id(new AphrontErrorView())
|
||||
->setTitle('There was an unknown error processing your card.')
|
||||
->setErrors(array('Please try again.'));
|
||||
$error = 'payment form submitted but no stripe token and no errors';
|
||||
$this->logStripeError($error);
|
||||
} else {
|
||||
// success -- do something with $stripe_token!!
|
||||
}
|
||||
} else if (!$error_view) {
|
||||
$error_view = id(new AphrontErrorView())
|
||||
->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
|
||||
->setTitle(
|
||||
'If you are using a test stripe key, use 4242424242424242, '.
|
||||
'any three digits for CVC, and any valid expiration date to '.
|
||||
'test!');
|
||||
}
|
||||
|
||||
$view = id(new AphrontPanelView())
|
||||
->setWidth(AphrontPanelView::WIDTH_FORM)
|
||||
->setHeader($title);
|
||||
|
||||
$form = id(new PhortuneStripePaymentFormView())
|
||||
->setUser($user)
|
||||
->setStripeKey($stripe_key)
|
||||
->setCardNumberError($card_number_error)
|
||||
->setCardCVCError($card_cvc_error)
|
||||
->setCardExpirationError($card_expiration_error);
|
||||
|
||||
$view->appendChild($form);
|
||||
|
||||
return
|
||||
$this->buildStandardPageResponse(
|
||||
array(
|
||||
$error_view,
|
||||
$view,
|
||||
),
|
||||
array(
|
||||
'title' => $title,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stripe JS and calls to Stripe handle all errors with processing this
|
||||
* form. This function takes the raw errors - in the form of an array
|
||||
* where each elementt is $type => $message - and figures out what if
|
||||
* any fields were invalid and pulls the messages into a flat object.
|
||||
*
|
||||
* See https://stripe.com/docs/api#errors for more information on possible
|
||||
* errors.
|
||||
*/
|
||||
private function parseRawErrors($errors) {
|
||||
$card_number_error = null;
|
||||
$card_cvc_error = null;
|
||||
$card_expiration_error = null;
|
||||
$messages = array();
|
||||
foreach ($errors as $index => $error) {
|
||||
$type = key($error);
|
||||
$msg = reset($error);
|
||||
$messages[] = $msg;
|
||||
switch ($type) {
|
||||
case 'number':
|
||||
case 'invalid_number':
|
||||
case 'incorrect_number':
|
||||
$card_number_error = true;
|
||||
break;
|
||||
case 'cvc':
|
||||
case 'invalid_cvc':
|
||||
case 'incorrect_cvc':
|
||||
$card_cvc_error = true;
|
||||
break;
|
||||
case 'expiry':
|
||||
case 'invalid_expiry_month':
|
||||
case 'invalid_expiry_year':
|
||||
$card_expiration_error = true;
|
||||
break;
|
||||
case 'card_declined':
|
||||
case 'expired_card':
|
||||
case 'duplicate_transaction':
|
||||
case 'processing_error':
|
||||
// these errors don't map well to field(s) being bad
|
||||
break;
|
||||
case 'invalid_amount':
|
||||
case 'missing':
|
||||
default:
|
||||
// these errors only happen if we (not the user) messed up so log it
|
||||
$error = sprintf(
|
||||
'error_type: %s error_message: %s',
|
||||
$type,
|
||||
$msg
|
||||
);
|
||||
$this->logStripeError($error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// append a helpful "fix this" to the messages to be displayed to the user
|
||||
if (count($messages) == 1) {
|
||||
$messages[] = 'Please fix this error and try again.';
|
||||
} else {
|
||||
$messages[] = 'Please fix these errors and try again.';
|
||||
}
|
||||
|
||||
return array(
|
||||
$card_number_error,
|
||||
$card_cvc_error,
|
||||
$card_expiration_error,
|
||||
$messages
|
||||
);
|
||||
}
|
||||
|
||||
private function logStripeError($message) {
|
||||
phlog('STRIPE-ERROR '.$message);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'applications/phortune/stripe/controller/base');
|
||||
phutil_require_module('phabricator', 'applications/phortune/stripe/view/paymentform');
|
||||
phutil_require_module('phabricator', 'view/form/error');
|
||||
phutil_require_module('phabricator', 'view/layout/panel');
|
||||
|
||||
phutil_require_module('phutil', 'error');
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
||||
phutil_require_source('PhortuneStripeTestPaymentFormController.php');
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class PhortuneStripePaymentFormView extends AphrontView {
|
||||
private $user;
|
||||
private $stripeKey;
|
||||
private $cardNumberError;
|
||||
private $cardCVCError;
|
||||
private $cardExpirationError;
|
||||
|
||||
public function setUser(PhabricatorUser $user) {
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
private function getUser() {
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setStripeKey($key) {
|
||||
$this->stripeKey = $key;
|
||||
return $this;
|
||||
}
|
||||
private function getStripeKey() {
|
||||
return $this->stripeKey;
|
||||
}
|
||||
|
||||
public function setCardNumberError($error) {
|
||||
$this->cardNumberError = $error;
|
||||
return $this;
|
||||
}
|
||||
private function getCardNumberError() {
|
||||
return $this->cardNumberError;
|
||||
}
|
||||
|
||||
public function setCardCVCError($error) {
|
||||
$this->cardCVCError = $error;
|
||||
return $this;
|
||||
}
|
||||
private function getCardCVCError() {
|
||||
return $this->cardCVCError;
|
||||
}
|
||||
|
||||
public function setCardExpirationError($error) {
|
||||
$this->cardExpirationError = $error;
|
||||
return $this;
|
||||
}
|
||||
private function getCardExpirationError() {
|
||||
return $this->cardExpirationError;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$form_id = celerity_generate_unique_node_id();
|
||||
require_celerity_resource('stripe-payment-form-css');
|
||||
require_celerity_resource('aphront-tooltip-css');
|
||||
Javelin::initBehavior('phabricator-tooltips');
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setID($form_id)
|
||||
->setUser($this->getUser())
|
||||
->appendChild(
|
||||
id(new AphrontFormMarkupControl())
|
||||
->setLabel('')
|
||||
->setValue(
|
||||
javelin_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'credit-card-logos',
|
||||
'sigil' => 'has-tooltip',
|
||||
'meta' => array(
|
||||
'tip' => 'We support Visa, Mastercard, American Express, '.
|
||||
'Discover, JCB, and Diners Club.',
|
||||
'size' => 440,
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel('Card Number')
|
||||
->setDisableAutocomplete(true)
|
||||
->setSigil('number-input')
|
||||
->setError($this->getCardNumberError())
|
||||
)
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel('CVC')
|
||||
->setDisableAutocomplete(true)
|
||||
->setSigil('cvc-input')
|
||||
->setError($this->getCardCVCError())
|
||||
)
|
||||
->appendChild(
|
||||
id(new PhortuneMonthYearExpiryControl())
|
||||
->setLabel('Expiration')
|
||||
->setUser($this->getUser())
|
||||
->setError($this->getCardExpirationError())
|
||||
)
|
||||
->appendChild(
|
||||
javelin_render_tag(
|
||||
'input',
|
||||
array(
|
||||
'hidden' => true,
|
||||
'name' => 'stripeToken',
|
||||
'sigil' => 'stripe-token-input',
|
||||
)
|
||||
)
|
||||
)
|
||||
->appendChild(
|
||||
javelin_render_tag(
|
||||
'input',
|
||||
array(
|
||||
'hidden' => true,
|
||||
'name' => 'cardErrors',
|
||||
'sigil' => 'card-errors-input'
|
||||
)
|
||||
)
|
||||
)
|
||||
->appendChild(
|
||||
phutil_render_tag(
|
||||
'input',
|
||||
array(
|
||||
'hidden' => true,
|
||||
'name' => 'stripeKey',
|
||||
'value' => $this->getStripeKey(),
|
||||
)
|
||||
)
|
||||
)
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue('Submit Payment')
|
||||
);
|
||||
|
||||
Javelin::initBehavior(
|
||||
'stripe-payment-form',
|
||||
array(
|
||||
'stripePublishKey' => $this->getStripeKey(),
|
||||
'root' => $form_id,
|
||||
)
|
||||
);
|
||||
|
||||
return $form->render();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
/**
|
||||
* This file is automatically generated. Lint this module to rebuild it.
|
||||
* @generated
|
||||
*/
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'applications/phortune/control/monthyearexpiry');
|
||||
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||
phutil_require_module('phabricator', 'view/base');
|
||||
phutil_require_module('phabricator', 'view/form/base');
|
||||
phutil_require_module('phabricator', 'view/form/control/markup');
|
||||
phutil_require_module('phabricator', 'view/form/control/submit');
|
||||
phutil_require_module('phabricator', 'view/form/control/text');
|
||||
|
||||
phutil_require_module('phutil', 'markup');
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
||||
phutil_require_source('PhortuneStripePaymentFormView.php');
|
|
@ -18,19 +18,39 @@
|
|||
|
||||
final class AphrontFormTextControl extends AphrontFormControl {
|
||||
|
||||
private $disableAutocomplete;
|
||||
private $sigil;
|
||||
|
||||
public function setDisableAutocomplete($disable) {
|
||||
$this->disableAutocomplete = $disable;
|
||||
return $this;
|
||||
}
|
||||
private function getDisableAutocomplete() {
|
||||
return $this->disableAutocomplete;
|
||||
}
|
||||
public function getSigil() {
|
||||
return $this->sigil;
|
||||
}
|
||||
public function setSigil($sigil) {
|
||||
$this->sigil = $sigil;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getCustomControlClass() {
|
||||
return 'aphront-form-control-text';
|
||||
}
|
||||
|
||||
protected function renderInput() {
|
||||
return phutil_render_tag(
|
||||
return javelin_render_tag(
|
||||
'input',
|
||||
array(
|
||||
'type' => 'text',
|
||||
'name' => $this->getName(),
|
||||
'value' => $this->getValue(),
|
||||
'disabled' => $this->getDisabled() ? 'disabled' : null,
|
||||
'id' => $this->getID(),
|
||||
'type' => 'text',
|
||||
'name' => $this->getName(),
|
||||
'value' => $this->getValue(),
|
||||
'disabled' => $this->getDisabled() ? 'disabled' : null,
|
||||
'autocomplete' => $this->getDisableAutocomplete() ? 'off' : null,
|
||||
'id' => $this->getID(),
|
||||
'sigil' => $this->getSigil(),
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||
phutil_require_module('phabricator', 'view/form/control/base');
|
||||
|
||||
phutil_require_module('phutil', 'markup');
|
||||
|
||||
|
||||
phutil_require_source('AphrontFormTextControl.php');
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* @provides stripe-payment-form-css
|
||||
*/
|
||||
|
||||
.credit-card-logos {
|
||||
background: url(/rsrc/image/credit_cards.png) no-repeat 0px 2px;
|
||||
height: 32px;
|
||||
}
|
BIN
webroot/rsrc/image/credit_cards.png
Normal file
BIN
webroot/rsrc/image/credit_cards.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* @provides javelin-behavior-stripe-payment-form
|
||||
* @requires javelin-behavior
|
||||
* javelin-dom
|
||||
* javelin-json
|
||||
* stripe-core
|
||||
*/
|
||||
|
||||
JX.behavior('stripe-payment-form', function(config) {
|
||||
Stripe.setPublishableKey(config.stripePublishKey);
|
||||
|
||||
var root = JX.$(config.root);
|
||||
var cardErrors = JX.DOM.find(root, 'input', 'card-errors-input');
|
||||
var stripeToken = JX.DOM.find(root, 'input', 'stripe-token-input');
|
||||
|
||||
var getCardData = function() {
|
||||
return {
|
||||
number : JX.DOM.find(root, 'input', 'number-input').value,
|
||||
cvc : JX.DOM.find(root, 'input', 'cvc-input' ).value,
|
||||
month : JX.DOM.find(root, 'select', 'month-input' ).value,
|
||||
year : JX.DOM.find(root, 'select', 'year-input' ).value
|
||||
};
|
||||
}
|
||||
|
||||
var stripeErrorObject = function(type) {
|
||||
var errorPre = 'Stripe (our payments provider) has detected your card ';
|
||||
var errorPost = ' is invalid.';
|
||||
var msg = '';
|
||||
var result = {};
|
||||
|
||||
switch (type) {
|
||||
case 'number':
|
||||
msg = errorPre + 'number' + errorPost;
|
||||
break;
|
||||
case 'cvc':
|
||||
msg = errorPre + 'CVC' + errorPost;
|
||||
break;
|
||||
case 'expiry':
|
||||
msg = errorPre + 'expiration date' + errorPost;
|
||||
break;
|
||||
case 'stripe':
|
||||
msg = 'Stripe (our payments provider) is experiencing issues. ' +
|
||||
'Please try again.';
|
||||
break;
|
||||
case 'invalid_request':
|
||||
default:
|
||||
msg = 'Unknown error.';
|
||||
// TODO - how best report bugs? would be good to get
|
||||
// user feedback since this shouldn't happen!
|
||||
break;
|
||||
}
|
||||
|
||||
result[type] = msg;
|
||||
return result;
|
||||
}
|
||||
|
||||
var onsubmit = function(e) {
|
||||
e.kill();
|
||||
|
||||
// validate the card data with Stripe client API and submit the form
|
||||
// with any detected errors
|
||||
var cardData = getCardData();
|
||||
var errors = [];
|
||||
if (!Stripe.validateCardNumber(cardData.number)) {
|
||||
errors.push(stripeErrorObject('number'));
|
||||
}
|
||||
if (!Stripe.validateCVC(cardData.cvc)) {
|
||||
errors.push(stripeErrorObject('cvc'));
|
||||
}
|
||||
if (!Stripe.validateExpiry(cardData.month,
|
||||
cardData.year)) {
|
||||
errors.push(stripeErrorObject('expiry'));
|
||||
}
|
||||
if (errors.length != 0) {
|
||||
cardErrors.value = JX.JSON.stringify(errors);
|
||||
root.submit();
|
||||
return true;
|
||||
}
|
||||
|
||||
// no errors detected so contact Stripe asynchronously
|
||||
var submitData = {
|
||||
number : cardData.number,
|
||||
cvc : cardData.cvc,
|
||||
exp_month : cardData.month,
|
||||
exp_year : cardData.year
|
||||
};
|
||||
Stripe.createToken(submitData, stripeResponseHandler);
|
||||
return false;
|
||||
}
|
||||
|
||||
var stripeResponseHandler = function(status, response) {
|
||||
if (response.error) {
|
||||
var errors = [];
|
||||
switch (response.error.type) {
|
||||
case 'card_error':
|
||||
var error = {};
|
||||
error[response.error.code] = response.error.message;
|
||||
errors.push(error);
|
||||
break;
|
||||
case 'invalid_request_error':
|
||||
errors.push(stripeErrorObject('invalid_request'));
|
||||
break;
|
||||
case 'api_error':
|
||||
default:
|
||||
errors.push(stripeErrorObject('stripe'));
|
||||
break;
|
||||
}
|
||||
cardErrors.value = JX.JSON.stringify(errors);
|
||||
} else {
|
||||
// success - we can use the token to create a customer object with
|
||||
// Stripe and let the billing commence!
|
||||
var token = response['id'];
|
||||
stripeToken.value = token;
|
||||
}
|
||||
root.submit();
|
||||
}
|
||||
|
||||
JX.DOM.listen(
|
||||
root,
|
||||
'submit',
|
||||
null,
|
||||
onsubmit);
|
||||
});
|
1
webroot/rsrc/js/stripe
Symbolic link
1
webroot/rsrc/js/stripe
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../../externals/stripe-js/
|
Loading…
Reference in a new issue