1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-22 14:52:41 +01:00

Remove support for Balanced payments

Summary: See <https://www.balancedpayments.com/stripe>. Just get rid of support since Phortune is a prototype anyway.

Test Plan: `grep`, poked around Phortune.

Reviewers: btrahan, chad

Reviewed By: chad

Subscribers: aurelijus, epriestley

Differential Revision: https://secure.phabricator.com/D12074
This commit is contained in:
epriestley 2015-03-13 15:47:37 -07:00
parent 446e92e5c3
commit 7d69d8ae6a
42 changed files with 2 additions and 4122 deletions

View file

@ -1,14 +0,0 @@
# composer
.buildpath
composer.lock
composer.phar
vendor
*~
*#
# phar
*.phar
# eclipse-pdt
.settings
.project
*.iml

View file

@ -1,8 +0,0 @@
language: php
before_script:
- curl -s http://getcomposer.org/installer | php
- php composer.phar install
script: phpunit --bootstrap vendor/autoload.php --exclude-group suite tests/
php:
- 5.3
- 5.4

View file

@ -1,22 +0,0 @@
Copyright (c) 2012 Balanced
MIT License
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.

View file

@ -1,156 +0,0 @@
# Balanced
Online Marketplace Payments
[![Build Status](https://secure.travis-ci.org/balanced/balanced-php.png)](http://travis-ci.org/balanced/balanced-php)
The design of this library was heavily influenced by [Httpful](https://github.com/nategood/httpful).
## Requirements
- [PHP](http://www.php.net) >= 5.3 **with** [cURL](http://www.php.net/manual/en/curl.installation.php)
- [RESTful](https://github.com/bninja/restful) >= 0.1
- [Httpful](https://github.com/nategood/httpful) >= 0.1
## Issues
Please use appropriately tagged github [issues](https://github.com/balanced/balanced-php/issues) to request features or report bugs.
## Installation
You can install using [composer](#composer), a [phar](#phar) package or from [source](#source). Note that Balanced is [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md) compliant:
### Composer
If you don't have Composer [install](http://getcomposer.org/doc/00-intro.md#installation) it:
$ curl -s https://getcomposer.org/installer | php
Add this to your `composer.json`:
{
"require": {
"balanced/balanced": "*"
}
}
Refresh your dependencies:
$ php composer.phar update
Then make sure to `require` the autoloader and initialize all:
<?php
require(__DIR__ . '/vendor/autoload.php');
\Httpful\Bootstrap::init();
\RESTful\Bootstrap::init();
\Balanced\Bootstrap::init();
...
### Phar
Download an Httpful [phar](http://php.net/manual/en/book.phar.php) file, which are all [here](https://github.com/nategood/httpful/downloads):
$ curl -s -L -o httpful.phar https://github.com/downloads/nategood/httpful/httpful.phar
Download a RESTful [phar](http://php.net/manual/en/book.phar.php) file, which are all [here](https://github.com/bninja/restful/downloads):
$ curl -s -L -o restful.phar https://github.com/bninja/restful/downloads/restful.phar
Download a Balanced [phar](http://php.net/manual/en/book.phar.php) file, which are all [here](https://github.com/balanced/balanced-php/downloads):
$ curl -s -L -o balanced.phar https://github.com/balanced/balanced-php/downloads/balanced-{VERSION}.phar
And then `include` all:
<?php
include(__DIR__ . '/httpful.phar');
include(__DIR__ . '/restful.phar');
include(__DIR__ . '/balanced.phar');
...
### Source
Download [Httpful](https://github.com/nategood/httpful) source:
$ curl -s -L -o httpful.zip https://github.com/nategood/httpful/zipball/master;
$ unzip httpful.zip; mv nategood-httpful* httpful; rm httpful.zip
Download [RESTful](https://github.com/bninja/restful) source:
$ curl -s -L -o restful.zip https://github.com/bninja/restful/zipball/master;
$ unzip restful.zip; mv bninja-restful* restful; rm restful.zips
Download the Balanced source:
$ curl -s -L -o balanced.zip https://github.com/balanced/balanced-php/zipball/master
$ unzip balanced.zip; mv balanced-balanced-php-* balanced; rm balanced.zip
And then `require` all bootstrap files:
<?php
require(__DIR__ . "/httpful/bootstrap.php")
require(__DIR__ . "/restful/bootstrap.php")
require(__DIR__ . "/balanced/bootstrap.php")
...
## Quickstart
curl -s http://getcomposer.org/installer | php
echo '{
"require": {
"balanced/balanced": "*"
}
}' > composer.json
php composer.phar install
curl https://raw.github.com/balanced/balanced-php/master/example/example.php > example.php
php example.php
curl https://raw.github.com/balanced/balanced-php/master/example/buyer-example.php > buyer-example.php
php -S 127.0.0.1:9321 buyer-example.php
# now open a browser and go to http://127.0.0.1:9321/ to view how to tokenize cards and add to a buyer
## Usage
See https://www.balancedpayments.com/docs/overview?language=php for tutorials and documentation.
## Testing
$ phpunit --bootstrap vendor/autoload.php tests/
Or if you'd like to skip network calls:
$ phpunit --exclude-group suite --bootstrap vendor/autoload.php tests/
## Publishing
1. Ensure that **all** [tests](#testing) pass
2. Increment minor `VERSION` in `src/Balanced/Settings` and `composer.json` (`git commit -am 'v{VERSION} release'`)
3. Tag it (`git tag -a v{VERSION} -m 'v{VERSION} release'`)
4. Push the tag (`git push --tag`)
5. [Packagist](http://packagist.org/packages/balanced/balanced) will see the new tag and take it from there
6. Build (`build-phar`) and upload a [phar](http://php.net/manual/en/book.phar.php) file
## Contributing
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Write your code **and [tests](#testing)**
4. Ensure all tests still pass (`phpunit --bootstrap vendor/autoload.php tests/`)
5. Commit your changes (`git commit -am 'Add some feature'`)
6. Push to the branch (`git push origin my-new-feature`)
7. Create new pull request
## Contributors
* [Jacob Rus](https://github.com/jrus)
* [Leon Smith](https://github.com/leonsmith)
* [Matt Drollette](https://github.com/MDrollette)
* [You](https://github.com/balanced/balanced-php/issues)!

View file

@ -1,4 +0,0 @@
<?php
require(__DIR__ . '/src/Balanced/Bootstrap.php');
\Balanced\Bootstrap::init();

View file

@ -1,36 +0,0 @@
#!/usr/bin/php
<?php
include('src/Balanced/Settings.php');
function exit_unless($condition, $msg = null) {
if ($condition)
return;
echo "[FAIL] $msg";
exit(1);
}
echo "Building Phar... ";
$base_dir = dirname(__FILE__);
$source_dir = $base_dir . '/src/Balanced/';
$phar_name = 'balanced.phar';
$phar_path = $base_dir . '/' . $phar_name;
$phar = new Phar($phar_path, 0, $phar_name);
$stub = <<<HEREDOC
<?php
// Phar Stub File
Phar::mapPhar('balanced.phar');
include('phar://balanced.phar/Balanced/Bootstrap.php');
\Balanced\Bootstrap::pharInit();
__HALT_COMPILER();
HEREDOC;
$phar->setStub($stub);
exit_unless($phar, "Unable to create a phar. Make sure you have phar.readonly=0 set in your ini file.");
$phar->buildFromDirectory(dirname($source_dir));
echo "[ OK ]\n";
echo "Renaming Phar... ";
$phar_versioned_name = 'balanced-' . \Balanced\Settings::VERSION . '.phar';
$phar_versioned_path = $base_dir . '/' . $phar_versioned_name;
rename($phar_path, $phar_versioned_path);
echo "[ OK ]\n";

View file

@ -1,24 +0,0 @@
{
"name": "balanced/balanced",
"description": "Client for Balanced API",
"homepage": "http://github.com/balanced/balanced-php",
"license": "MIT",
"keywords": ["payments", "api"],
"version": "0.7.1",
"authors": [
{
"name": "Balanced",
"email": "dev@balancedpayments.com",
"homepage": "http://www.balancedpayments.com"
}
],
"require": {
"nategood/httpful": "*",
"bninja/restful": "*"
},
"autoload": {
"psr-0": {
"Balanced": "src/"
}
}
}

View file

@ -1,54 +0,0 @@
<?php
//
// Learn how to authenticate a bank account so you can debit with it.
//
require(__DIR__ . '/vendor/autoload.php');
Httpful\Bootstrap::init();
RESTful\Bootstrap::init();
Balanced\Bootstrap::init();
// create a new marketplace
$key = new Balanced\APIKey();
$key->save();
Balanced\Settings::$api_key = $key->secret;
$marketplace = new Balanced\Marketplace();
$marketplace->save();
// create a bank account
$bank_account = $marketplace->createBankAccount("Jack Q Merchant",
"123123123",
"123123123"
);
$buyer = $marketplace->createAccount("buyer@example.org");
$buyer->addBankAccount($bank_account);
print("you can't debit from a bank account until you verify it\n");
try {
$buyer->debit(100);
} catch (Exception $e) {
printf("Debit failed, %s\n", $e->getMessage());
}
// authenticate
$verification = $bank_account->verify();
try {
$verification->confirm(1, 2);
} catch (Balanced\Errors\BankAccountVerificationFailure $e) {
printf('Authentication error , %s\n', $e->getMessage());
print("PROTIP: for TEST bank accounts the valid amount is always 1 and 1\n");
}
$verification->confirm(1, 1);
$debit = $buyer->debit(100);
printf("debited the bank account %s for %d cents\n",
$debit->source->uri,
$debit->amount
);
print("and there you have it");
?>

View file

@ -1,157 +0,0 @@
<?php
require(__DIR__ . '/vendor/autoload.php');
Httpful\Bootstrap::init();
RESTful\Bootstrap::init();
Balanced\Bootstrap::init();
$API_KEY_SECRET = '5f4db668a5ec11e1b908026ba7e239a9';
$page = $_SERVER['REQUEST_URI'];
Balanced\Settings::$api_key = $API_KEY_SECRET;
$marketplace = Balanced\Marketplace::mine();
if ($page == '/') {
// do nothing
} elseif ($page == '/buyer') {
if (isset($_POST['uri']) and isset($_POST['email_address'])) {
// create in balanced
$email_address = $_POST['email_address'];
$card_uri = $_POST['uri'];
try {
echo create_buyer($email_address, $card_uri)->uri;
return;
} catch (Balanced\Errors\Error $e) {
echo $e->getMessage();
return;
}
}
}
function create_buyer($email_address, $card_uri) {
$marketplace = Balanced\Marketplace::mine();
try {
# new buyer
$buyer = $marketplace->createBuyer(
$email_address,
$card_uri);
}
catch (Balanced\Errors\DuplicateAccountEmailAddress $e) {
# oops, account for $email_address already exists so just add the card
$buyer = Balanced\Account::get($e->extras->account_uri);
$buyer->addCard($card_uri);
}
return $buyer;
}
?>
<html>
<head>
<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css" type="text/css">
<style type="text/css">
[name="marketplace_eid"] {
width: 300px;
}
[name^="expiration"] {
width: 50px;
}
[name="security_code"] {
width: 50px;
}
code { display: block; }
pre { color: green; }
</style>
</head>
<body>
<h1>Balanced Sample - Collect Credit Card Information</h1>
<div class="row">
<div class="span6">
<form id="payment">
<div>
<label>Email Address</label>
<input name="email_address" value="bob@example.com">
</div>
<div>
<label>Card Number</label>
<input name="card_number" value="4111111111111111" autocomplete="off">
</div>
<div>
<label>Expiration</label>
<input name="expiration_month" value="1"> / <input name="expiration_year" value="2020">
</div>
<div>
<label>Security Code</label>
<input name="security_code" value="123" autocomplete="off">
</div>
<button>Submit Payment Data</button>
</form>
</div>
</div>
<div id="result"></div>
<script type="text/javascript" src="https://js.balancedpayments.com/v1/balanced.js"></script>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript">
var marketplaceUri = '<?php echo $marketplace->uri; ?>';
var debug = function (tag, content) {
$('<' + tag + '>' + content + '</' + tag + '>').appendTo('#result');
};
try {
balanced.init(marketplaceUri);
} catch (e) {
debug('code', 'You need to set the marketplaceUri variable');
}
function accountCreated(response) {
debug('code', 'account create result: ' + response);
}
function balancedCallback(response) {
var tag = (response.status < 300) ? 'pre' : 'code';
debug(tag, JSON.stringify(response));
switch (response.status) {
case 201:
// response.data.uri == uri of the card resource, submit to your server
$.post('/buyer', {
uri: response.data.uri,
email_address: $('[name="email_address"]').val()
}, accountCreated);
case 400:
case 403:
// missing/malformed data - check response.error for details
break;
case 402:
// we couldn't authorize the buyer's credit card - check response.error for details
break;
case 404:
// your marketplace URI is incorrect
break;
default:
// we did something unexpected - check response.error for details
break;
}
}
var tokenizeCard = function(e) {
e.preventDefault();
var $form = $('form#payment');
var cardData = {
card_number: $form.find('[name="card_number"]').val(),
expiration_month: $form.find('[name="expiration_month"]').val(),
expiration_year: $form.find('[name="expiration_year"]').val(),
security_code: $form.find('[name="security_code"]').val()
};
balanced.card.create(cardData, balancedCallback);
};
$('#payment').submit(tokenizeCard);
if (window.location.protocol === 'file:') {
alert("balanced.js does not work when included in pages served over file:// URLs. Try serving this page over a webserver. Contact support@balancedpayments.com if you need assistance.");
}
</script>
</body>
</html>

View file

@ -1,5 +0,0 @@
{
"require": {
"balanced/balanced": "*"
}
}

View file

@ -1,42 +0,0 @@
<?php
require('vendor/autoload.php');
Httpful\Bootstrap::init();
RESTful\Bootstrap::init();
Balanced\Bootstrap::init();
$API_KEY_SECRET = '5f4db668a5ec11e1b908026ba7e239a9';
Balanced\Settings::$api_key = $API_KEY_SECRET;
$marketplace = Balanced\Marketplace::mine();
print "create a card\n";
$card = $marketplace->cards->create(array(
"card_number" => "5105105105105100",
"expiration_month" => "12",
"expiration_year" => "2015"
));
print "our card: " . $card->uri . "\n";
print "create a **buyer** account with that card\n";
$buyer = $marketplace->createBuyer(null, $card->uri);
print "our buyer account: " . $buyer->uri . "\n";
print "debit our buyer, let's say $15\n";
try {
$debit = $buyer->debit(1500);
print "our buyer debit: " . $debit->uri . "\n";
}
catch (Balanced\Errors\Declined $e) {
print "oh no, the processor declined the debit!\n";
}
catch (Balanced\Errors\NoFundingSource $e) {
print "oh no, the buyer has not active funding sources!\n";
}
catch (Balanced\Errors\CannotDebit $e) {
print "oh no, the buyer has no debitable funding sources!\n";
}
print "and there you have it 8)\n";
?>

View file

@ -1,59 +0,0 @@
<?php
/*
* Welcome weary traveller. Sick of polling for state changes? Well today have
* I got good news for you. Run this example below to see how to get yourself
* some callback goodness and to understand how events work.
*/
require(__DIR__ . "/vendor/autoload.php");
Httpful\Bootstrap::init();
RESTful\Bootstrap::init();
Balanced\Bootstrap::init();
// create a new marketplace
$key = new Balanced\APIKey();
$key->save();
Balanced\Settings::$api_key = $key->secret;
$marketplace = new Balanced\Marketplace();
$marketplace->save();
// let"s create a requestb.in
$ch = curl_init("http://requestb.in/api/v1/bins");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . 0)
);
$result = json_decode(curl_exec($ch));
$bin_name = $result->name;
$callback_url = "http://requestb.in/" . $bin_name;
$requests_url = "http://requestb.in/api/v1/bins/" . $bin_name . "/requests";
printf("let's create a callback\n");
$marketplace->createCallback($callback_url);
printf("let's create a card and associate it with a new account\n");
$card = $marketplace->cards->create(array(
"card_number" => "5105105105105100",
"expiration_month" => "12",
"expiration_year" => "2015"
));
$buyer = $marketplace->createBuyer("buyer@example.org", $card->uri);
printf("generate a debit (which implicitly creates and captures a hold)\n");
$buyer->debit(100);
foreach ($marketplace->events as $event) {
printf("this was a %s event, it occurred at %s\n",
$event->type,
$event->occurred_at
);
}
printf("ok, let's check with requestb.in to see if our callbacks fired at %s\n", $callback_url);
printf("we received callbacks, you can view them at http://requestb.in/%s?inspect\n",
$bin_name
);
?>

View file

@ -1,120 +0,0 @@
<?php
require('vendor/autoload.php');
Httpful\Bootstrap::init();
RESTful\Bootstrap::init();
Balanced\Bootstrap::init();
print "create our new api key\n";
$key = new Balanced\APIKey();
$key->save();
print "Our secret is " . $key->secret . "\n";
print "configure with our secret " . $key->secret . "\n";
Balanced\Settings::$api_key = $key->secret;
print "create our marketplace";
$marketplace = new Balanced\Marketplace();
$marketplace->save();
if (Balanced\Merchant::me() == null) {
throw new Exception("Balanced\Merchant::me() should not be null");
}
print "What's my merchant? Easy: Balanced\Merchant::me(): " . Balanced\Merchant::me()->uri . "\n";
if (Balanced\Marketplace::mine() == null) {
throw new Exception("Balanced\Marketplace::mine() should never be null");
}
print "What's my marketplace? Easy: Balanced\Marketplace::mine(): " .Balanced\Marketplace::mine()->uri . "\n";
print "My marketplace's name is " . $marketplace->name . "\n";
print "Changing it to TestFooey\n";
$marketplace->name = "TestFooey";
$marketplace->save();
print "My marketplace name is now " . $marketplace->name . "\n";
if ($marketplace->name != "TestFooey") {
throw new Exception("Marketplace name is NOT TestFooey");
}
print "Cool, let's create a card\n";
$card = $marketplace->cards->create(array(
"card_number" => "5105105105105100",
"expiration_month" => "12",
"expiration_year" => "2015"
));
print "Our card: " . $card->uri . "\n";
print "Create out **buyer** account\n";
$buyer = $marketplace->createBuyer("buyer@example.org", $card->uri);
print "our buyer account: " . $buyer->uri . "\n";
print "hold some amount of funds on the buyer, let's say $15\n";
$the_hold = $buyer->hold(1500);
print "ok, no more holds! let's capture it (for the full amount)\n";
$debit = $the_hold->capture();
print "hmm, ho much money do i have in escrow? it should equal the debit amount\n";
$marketplace = Balanced\Marketplace::mine();
if ($marketplace->in_escrow != 1500) {
throw new Exception("1500 is not in escrow! This is wrong");
}
print "I have " . $marketplace->in_escrow . " in escrow!\n";
print "Cool. now let me refund the full amount";
$refund = $debit->refund();
print "ok, we have a merchant that's signing up, let's create an account for them first, let's create their bank account\n";
$bank_account = $marketplace->createBankAccount("Jack Q Merchant",
"123123123", /* account_number */
"123123123" /* bank_code (routing number is USA)*/
);
$identity = array(
"type" => "person",
"name" => "Billy Jones",
"street_address" => "801 High St",
"postal_code" => "94301",
"country" => "USA",
"dob" => "1979-02",
"phone_number" => "+16505551234"
);
$merchant = $marketplace->createMerchant('merchant@example.org',
$identity,
$bank_account->uri
);
print "our buyer is interested in buying something for $130\n";
$another_debit = $buyer->debit(13000, "MARKETPLACE.COM");
print "let's credit our merchant $110\n";
$credit = $merchant->credit(11000, "Buyer purchase something on Marketplace.com");
print "let's assume the marketplace charges 15%, so it earned $20\n";
$mp_credit = $marketplace->owner_account->credit(2000,
"Commission from MARKETPLACE.COM");
print "ok, let's invalidate the card used so it cannot be used again\n";
$card->is_valid = false;
$card->save();
print "how do we look up an existing object from the URI?\n";
$the_buyer = Balanced\Account::get($buyer->uri);
print "we got the buyer " . $the_buyer->email_address . "\n";
$the_debit = Balanced\Debit::get($debit->uri);
print "we got the debit: " . $the_debit->uri . "\n";
$the_credit = Balanced\Credit::get($credit->uri);
print "we got the credit: " . $the_credit->uri . "\n";
print "and there you have it :)\n";
?>

View file

@ -1,71 +0,0 @@
<?
require('vendor/autoload.php');
Httpful\Bootstrap::init();
RESTful\Bootstrap::init();
Balanced\Bootstrap::init();
$key = new Balanced\APIKey();
$key->save();
Balanced\Settings::$api_key = $key->secret;
$marketplace = new Balanced\Marketplace();
$marketplace->save();
$card = $marketplace->cards->create(array(
"card_number" => "5105105105105100",
"expiration_month" => "12",
"expiration_year" => "2015"
));
$buyer = $marketplace->createBuyer("buyer@example.com", $card->uri);
$debit = $buyer->debit(1500);
$debit->refund(100);
$debit->refund(100);
$debit->refund(100);
echo $debit->refunds->total() . " refunds" . "\n";
$total = 0;
foreach ($debit->refunds as $r) {
$total += $r->amount;
print "refund = " . $r->amount . "\n";
}
print $total . "\n";
# bigger pagination example
print "Create 60 **buyer** with cards accounts\n";
for ($i = 0; $i < 60; $i++) {
$card = $marketplace->cards->create(array(
"card_number" => "5105105105105100",
"expiration_month" => "12",
"expiration_year" => "2015"
));
$buyer = $marketplace->createBuyer("buyer" . $i . "@example.org", $card->uri);
print '.';
}
print "\n";
$cards = $marketplace->cards;
print $cards->total() . " cards in Marketplace\n";
foreach ($cards as $c) {
print "card " . $c->uri . "\n";
}
# let's iterate through cards for just a single account
foreach ($buyer->cards as $c) {
print "buyer's card " . $c->uri . "\n";
}
print "and there you have it :)\n";
?>

View file

@ -1,14 +0,0 @@
<?php
// run this file to test your composer install of Balanced
require(__DIR__ . '/vendor/autoload.php');
\Httpful\Bootstrap::init();
\RESTful\Bootstrap::init();
\Balanced\Bootstrap::init();
echo "[ OK ]\n";
echo "balanced version -- " . \Balanced\Settings::VERSION . " \n";
echo "restful version -- " . \RESTful\Settings::VERSION . " \n";
echo "httpful version -- " . \Httpful\Httpful::VERSION . " \n";

View file

@ -1,12 +0,0 @@
<?php
// run this file to test your phar install of Balanced
include(__DIR__ . '/httpful.phar');
include(__DIR__ . '/restful.phar');
include(__DIR__ . '/balanced.phar');
echo "[ OK ]\n";
echo "balanced version -- " . \Balanced\Settings::VERSION . " \n";
echo "restful version -- " . \RESTful\Settings::VERSION . " \n";
echo "httpful version -- " . \Httpful\Httpful::VERSION . " \n";

View file

@ -1,12 +0,0 @@
<?php
// run this file to test your source install of Balanced
require(__DIR__ . "/httpful/bootstrap.php");
require(__DIR__ . "/restful/bootstrap.php");
require(__DIR__ . "/balanced/bootstrap.php");
echo "[ OK ]\n";
echo "balanced version -- " . \Balanced\Settings::VERSION . " \n";
echo "restful version -- " . \RESTful\Settings::VERSION . " \n";
echo "httpful version -- " . \Httpful\Httpful::VERSION . " \n";

View file

@ -1,55 +0,0 @@
<?php
namespace Balanced;
use Balanced\Resource;
use Balanced\Settings;
use \RESTful\URISpec;
/**
* Represents an api key. These are used to authenticate you with the api.
*
* Typically you create an initial api key:
*
* <code>
* print \Balanced\Settings::$api_key == null;
* $api_key = new \Balanced\APIKey();
* $api_key = api_key->save();
* $secret = $api_key->secret;
* print $secret;
* </code>
*
* Then save the returned secret (we don't store it) and configure the client
* to use it:
*
* <code>
* \Balanced\Settings::$api_key = 'my-api-key-secret';
* </code>
*
* You can later add another api key if you'd like to rotate or expire old
* ones:
*
* <code>
* $api_key = new \Balanced\APIKey();
* $api_key = api_key->save();
* $new_secret = $api_key->secret;
* print $new_secret;
*
* \Balanced\Settings::$api_key = $new_secret;
*
* \Balanced\APIKey::query()
* ->sort(\Balanced\APIKey::f->created_at->desc())
* ->first()
* ->delete();
* </code>
*/
class APIKey extends Resource
{
protected static $_uri_spec = null;
public static function init()
{
self::$_uri_spec = new URISpec('api_keys', 'id', '/v1');
self::$_registry->add(get_called_class());
}
}

View file

@ -1,217 +0,0 @@
<?php
namespace Balanced;
use Balanced\Resource;
use \RESTful\URISpec;
/**
* Represent a buyer or merchant account on a marketplace.
*
* You create these using Balanced\Marketplace->createBuyer or
* Balanced\Marketplace->createMerchant.
*
* <code>
* $marketplace = \Balanced\Marketplace::mine();
*
* $card = $marketplace->cards->create(array(
* 'street_address' => $street_address,
* 'city' => 'Jollywood',
* 'region' => 'CA',
* 'postal_code' => '90210',
* 'name' => 'Captain Chunk',
* 'card_number' => '4111111111111111',
* 'expiration_month' => 7,
* 'expiration_year' => 2015
* ));
*
* $buyer = $marketplace->createBuyer(
* 'buyer@example.com',
* $card->uri,
* array(
* 'my_id' => '1212121',
* )
* );
* </code>
*
* @see Balanced\Marketplace->createBuyer
* @see Balanced\Marketplace->createMerchant
*/
class Account extends Resource
{
protected static $_uri_spec = null;
public static function init()
{
self::$_uri_spec = new URISpec('accounts', 'id');
self::$_registry->add(get_called_class());
}
/**
* Credit the account.
*
* @param int amount Amount to credit the account in USD pennies.
* @param string description Optional description of the credit.
* @param array[string]string meta Optional metadata to associate with the credit.
* @param mixed destination Optional URI of a funding destination (i.e. \Balanced\BankAccount) associated with this account to credit. If not specified the funding destination most recently added to the account is used.
* @param string appears_on_statement_as Optional description of the credit as it will appears on the customer's billing statement.
*
* @return \Balanced\Credit
*/
public function credit(
$amount,
$description = null,
$meta = null,
$destination = null,
$appears_on_statement_as = null)
{
if ($destination == null)
$destination_uri = null;
else
$destination_uri = is_string($destination) ? $destination : $destination->uri;
return $this->credits->create(array(
'amount' => $amount,
'description' => $description,
'meta' => $meta,
'destination_uri' => $destination_uri,
'appears_on_statement_as' => $appears_on_statement_as
));
}
/**
* Debit the account.
*
* @param int amount Amount to debit the account in USD pennies.
* @param string appears_on_statement_as Optional description of the debit as it will appears on the customer's billing statement.
* @param string description Optional description of the debit.
* @param array[string]string meta Optional metadata to associate with the debit.
* @param mixed Optional funding source (i.e. \Balanced\Card) or URI of a funding source associated with this account to debit. If not specified the funding source most recently added to the account is used.
*
* @return \Balanced\Debit
*/
public function debit(
$amount,
$appears_on_statement_as = null,
$description = null,
$meta = null,
$source = null,
$on_behalf_of = null)
{
if ($source == null) {
$source_uri = null;
} else if (is_string($source)) {
$source_uri = $source;
} else {
$source_uri = $source->uri;
}
if ($on_behalf_of == null) {
$on_behalf_of_uri = null;
} else if (is_string($on_behalf_of)) {
$on_behalf_of_uri = $on_behalf_of;
} else {
$on_behalf_of_uri = $on_behalf_of->uri;
}
if (isset($this->uri) && $on_behalf_of_uri == $this->uri)
throw new \InvalidArgumentException(
'The on_behalf_of parameter MAY NOT be the same account as the account you are debiting!'
);
return $this->debits->create(array(
'amount' => $amount,
'description' => $description,
'meta' => $meta,
'source_uri' => $source_uri,
'on_behalf_of_uri' => $on_behalf_of_uri,
'appears_on_statement_as' => $appears_on_statement_as
));
}
/**
* Create a hold (i.e. a guaranteed pending debit) for account funds. You
* can later capture or void. A hold is associated with a account funding
* source (i.e. \Balanced\Card). If you don't specify the source then the
* current primary funding source for the account is used.
*
* @param int amount Amount of the hold in USD pennies.
* @param string Optional description Description of the hold.
* @param string Optional URI referencing the card to use for the hold.
* @param array[string]string meta Optional metadata to associate with the hold.
*
* @return \Balanced\Hold
*/
public function hold(
$amount,
$description = null,
$source_uri = null,
$meta = null)
{
return $this->holds->create(array(
'amount' => $amount,
'description' => $description,
'source_uri' => $source_uri,
'meta' => $meta
));
}
/**
* Creates or associates a created card with the account. The default
* funding source for the account will be this card.
*
* @see \Balanced\Marketplace->createCard
*
* @param mixed card \Balanced\Card or URI referencing a card to associate with the account. Alternatively it can be an associative array describing a card to create and associate with the account.
*
* @return \Balanced\Account
*/
public function addCard($card)
{
if (is_string($card))
$this->card_uri = $card;
else if (is_array($card))
$this->card = $card;
else
$this->card_uri = $card->uri;
return $this->save();
}
/**
* Creates or associates a created bank account with the account. The
* new default funding destination for the account will be this bank account.
*
* @see \Balanced\Marketplace->createBankAccount
*
* @param mixed bank_account \Balanced\BankAccount or URI for a bank account to associate with the account. Alternatively it can be an associative array describing a bank account to create and associate with the account.
*
* @return \Balanced\Account
*/
public function addBankAccount($bank_account)
{
if (is_string($bank_account))
$this->bank_account_uri = $bank_account;
else if (is_array($bank_account))
$this->bank_account = $bank_account;
else
$this->bank_account_uri = $bank_account->uri;
return $this->save();
}
/**
* Promotes a role-less or buyer account to a merchant.
*
* @see Balanced\Marketplace::createMerchant
*
* @param mixed merchant Associative array describing the merchants identity or a URI referencing a created merchant.
*
* @return \Balanced\Account
*/
public function promoteToMerchant($merchant)
{
if (is_string($merchant))
$this->merchant_uri = $merchant;
else
$this->merchant = $merchant;
return $this->save();
}
}

View file

@ -1,127 +0,0 @@
<?php
namespace Balanced;
use Balanced\Resource;
use \RESTful\URISpec;
/**
* Represents an account bank account.
*
* You can create these via Balanced\Marketplace::bank_accounts::create or
* Balanced\Marketplace::createBankAccount. Associate them with a buyer or
* merchant one creation via Balanced\Marketplace::createBuyer or
* Balanced\Marketplace::createMerchant and with an existing buyer or merchant
* use Balanced\Account::addBankAccount.
*
* <code>
* $marketplace = \Balanced\Marketplace::mine();
*
* $bank_account = $marketplace->bank_accounts->create(array(
* 'name' => 'name',
* 'account_number' => '11223344',
* 'bank_code' => '1313123',
* ));
*
* $account = $marketplace
* ->accounts
* ->query()
* ->filter(Account::f->email_address->eq('merchant@example.com'))
* ->one();
* $account->addBankAccount($bank_account->uri);
* </code>
*/
class BankAccount extends Resource
{
protected static $_uri_spec = null;
public static function init()
{
self::$_uri_spec = new URISpec('bank_accounts', 'id', '/v1');
self::$_registry->add(get_called_class());
}
/**
* Credit a bank account.
*
* @param int amount Amount to credit in USD pennies.
* @param string description Optional description of the credit.
* @param string appears_on_statement_as Optional description of the credit as it will appears on the customer's billing statement.
*
* @return \Balanced\Credit
*
* <code>
* $bank_account = new \Balanced\BankAccount(array(
* 'account_number' => '12341234',
* 'name' => 'Fit Finlay',
* 'bank_code' => '325182797',
* 'type' => 'checking',
* ));
*
* $credit = $bank_account->credit(123, 'something descriptive');
* </code>
*/
public function credit(
$amount,
$description = null,
$meta = null,
$appears_on_statement_as = null)
{
if (!property_exists($this, 'account') || $this->account == null) {
$credit = $this->credits->create(array(
'amount' => $amount,
'description' => $description,
));
} else {
$credit = $this->account->credit(
$amount,
$description,
$meta,
$this->uri,
$appears_on_statement_as
);
}
return $credit;
}
public function verify()
{
$response = self::getClient()->post(
$this->verifications_uri, null
);
$verification = new BankAccountVerification();
$verification->_objectify($response->body);
return $verification;
}
}
/**
* Represents an verification for a bank account which is a pre-requisite if
* you want to create debits using the associated bank account. The side-effect
* of creating a verification is that 2 random amounts will be deposited into
* the account which must then be confirmed via the confirm method to ensure
* that you have access to the bank account in question.
*
* You can create these via Balanced\Marketplace::bank_accounts::verify.
*
* <code>
* $marketplace = \Balanced\Marketplace::mine();
*
* $bank_account = $marketplace->bank_accounts->create(array(
* 'name' => 'name',
* 'account_number' => '11223344',
* 'bank_code' => '1313123',
* ));
*
* $verification = $bank_account->verify();
* </code>
*/
class BankAccountVerification extends Resource {
public function confirm($amount1, $amount2) {
$this->amount_1 = $amount1;
$this->amount_2 = $amount2;
$this->save();
return $this;
}
}

View file

@ -1,79 +0,0 @@
<?php
namespace Balanced;
/**
* Bootstrapper for Balanced does autoloading and resource initialization.
*/
class Bootstrap
{
const DIR_SEPARATOR = DIRECTORY_SEPARATOR;
const NAMESPACE_SEPARATOR = '\\';
public static $initialized = false;
public static function init()
{
spl_autoload_register(array('\Balanced\Bootstrap', 'autoload'));
self::initializeResources();
}
public static function autoload($classname)
{
self::_autoload(dirname(dirname(__FILE__)), $classname);
}
public static function pharInit()
{
spl_autoload_register(array('\Balanced\Bootstrap', 'pharAutoload'));
self::initializeResources();
}
public static function pharAutoload($classname)
{
self::_autoload('phar://balanced.phar', $classname);
}
private static function _autoload($base, $classname)
{
if (!strncmp($classname, 'Balanced\Errors\\', strlen('Balanced\Errors\\')))
$classname = 'Balanced\Errors';
$parts = explode(self::NAMESPACE_SEPARATOR, $classname);
$path = $base . self::DIR_SEPARATOR. implode(self::DIR_SEPARATOR, $parts) . '.php';
if (file_exists($path)) {
require_once($path);
}
}
/**
* Initializes resources (i.e. registers them with Resource::_registry). Note
* that if you add a Resource then you must initialize it here.
*
* @internal
*/
private static function initializeResources()
{
if (self::$initialized)
return;
\Balanced\Errors\Error::init();
\Balanced\Resource::init();
\Balanced\APIKey::init();
\Balanced\Marketplace::init();
\Balanced\Account::init();
\Balanced\Credit::init();
\Balanced\Debit::init();
\Balanced\Refund::init();
\Balanced\Card::init();
\Balanced\BankAccount::init();
\Balanced\Hold::init();
\Balanced\Merchant::init();
\Balanced\Callback::init();
\Balanced\Event::init();
self::$initialized = true;
}
}

View file

@ -1,24 +0,0 @@
<?php
namespace Balanced;
use Balanced\Resource;
use \RESTful\URISpec;
/*
* A Callback is a publicly accessible location that can receive POSTed JSON
* data whenever an Event is generated.
*
* You create these using Balanced\Marketplace->createCallback.
*
*/
class Callback extends Resource
{
protected static $_uri_spec = null;
public static function init()
{
self::$_uri_spec = new URISpec('callbacks', 'id');
self::$_registry->add(get_called_class());
}
}

View file

@ -1,61 +0,0 @@
<?php
namespace Balanced;
use Balanced\Resource;
use \RESTful\URISpec;
/**
* Represents an account card.
*
* You can create these via Balanced\Marketplace::cards::create or
* Balanced\Marketplace::createCard. Associate them with a buyer or merchant
* one creation via Marketplace::createBuyer or
* Balanced\Marketplace::createMerchant and with an existing buyer or merchant
* use Balanced\Account::addCard.
*
* <code>
* $marketplace = \Balanced\Marketplace::mine();
*
* $card = $marketplace->cards->create(array(
* 'name' => 'name',
* 'account_number' => '11223344',
* 'bank_code' => '1313123'
* ));
*
* $account = $marketplace
* ->accounts
* ->query()
* ->filter(Account::f->email_address->eq('buyer@example.com'))
* ->one();
* $account->addCard($card->uri);
* </code>
*/
class Card extends Resource
{
protected static $_uri_spec = null;
public static function init()
{
self::$_uri_spec = new URISpec('cards', 'id', '/v1');
self::$_registry->add(get_called_class());
}
public function debit(
$amount,
$appears_on_statement_as = null,
$description = null,
$meta = null,
$source = null)
{
if ($this->account == null) {
throw new \UnexpectedValueException('Card is not associated with an account.');
}
return $this->account->debit(
$amount,
$appears_on_statement_as,
$description,
$meta,
$this->uri);
}
}

View file

@ -1,75 +0,0 @@
<?php
namespace Balanced;
use Balanced\Resource;
use \RESTful\URISpec;
/**
* Represents an account credit transaction.
*
* You create these using Balanced\Account::credit.
*
* <code>
* $marketplace = \Balanced\Marketplace::mine();
*
* $account = $marketplace
* ->accounts
* ->query()
* ->filter(Account::f->email_address->eq('merchant@example.com'))
* ->one();
*
* $credit = $account->credit(
* 100,
* 'how it '
* array(
* 'my_id': '112233'
* )
* );
* </code>
*/
class Credit extends Resource
{
protected static $_uri_spec = null;
public static function init()
{
self::$_uri_spec = new URISpec('credits', 'id', '/v1');
self::$_registry->add(get_called_class());
}
/**
* Credit an unstored bank account.
*
* @param int amount Amount to credit in USD pennies.
* @param string description Optional description of the credit.
* @param mixed bank_account Associative array describing a bank account to credit. The bank account will *not* be stored.
*
* @return \Balanced\Credit
*
* <code>
* $credit = \Balanced\Credit::bankAccount(
* 123,
* array(
* 'account_number' => '12341234',
* 'name' => 'Fit Finlay',
* 'bank_code' => '325182797',
* 'type' => 'checking',
* ),
* 'something descriptive');
* </code>
*/
public static function bankAccount(
$amount,
$bank_account,
$description = null)
{
$credit = new Credit(array(
'amount' => $amount,
'bank_account' => $bank_account,
'description' => $description
));
$credit->save();
return $credit;
}
}

View file

@ -1,64 +0,0 @@
<?php
namespace Balanced;
use Balanced\Resource;
use \RESTful\URISpec;
/**
* Represents an account debit transaction.
*
* You create these using Balanced\Account::debit.
*
* <code>
* $marketplace = \Balanced\Marketplace::mine();
*
* $account = $marketplace
* ->accounts
* ->query()
* ->filter(Account::f->email_address->eq('buyer@example.com'))
* ->one();
*
* $debit = $account->debit(
* 100,
* 'how it appears on the statement',
* 'a description',
* array(
* 'my_id': '443322'
* )
* );
* </code>
*/
class Debit extends Resource
{
protected static $_uri_spec = null;
public static function init()
{
self::$_uri_spec = new URISpec('debits', 'id');
self::$_registry->add(get_called_class());
}
/**
* Create a refund for this debit. You can create multiple refunds for a
* debit but the total amount of the refunds must be less than the debit
* amount.
*
* @param int amount Optional amount of the refund in USD pennies. If unspecified then the full debit amount is used.
* @param string description Optional description of the refund.
* @param array[string]string meta Optional metadata to associate with the refund.
*
* @return \Balanced\Refund
*/
public function refund(
$amount = null,
$description = null,
$meta = null)
{
return $this->refunds->create(array(
'amount' => $amount,
'description' => $description,
'meta' => $meta
));
}
}

View file

@ -1,135 +0,0 @@
<?php
namespace Balanced\Errors;
use RESTful\Exceptions\HTTPError;
class Error extends HTTPError
{
public static $codes = array();
public static function init()
{
foreach (get_declared_classes() as $class) {
$parent_class = get_parent_class($class);
if ($parent_class != 'Balanced\Errors\Error')
continue;
foreach ($class::$codes as $type)
self::$codes[$type] = $class;
}
}
}
class DuplicateAccountEmailAddress extends Error
{
public static $codes = array('duplicate-email-address');
}
class InvalidAmount extends Error
{
public static $codes = array('invalid-amount');
}
class InvalidRoutingNumber extends Error
{
public static $codes = array('invalid-routing-number');
}
class InvalidBankAccountNumber extends Error
{
public static $codes = array('invalid-bank-account-number');
}
class Declined extends Error
{
public static $codes = array('funding-destination-declined', 'authorization-failed');
}
class CannotAssociateMerchantWithAccount extends Error
{
public static $codes = array('cannot-associate-merchant-with-account');
}
class AccountIsAlreadyAMerchant extends Error
{
public static $codes = array('account-already-merchant');
}
class NoFundingSource extends Error
{
public static $codes = array('no-funding-source');
}
class NoFundingDestination extends Error
{
public static $codes = array('no-funding-destination');
}
class CardAlreadyAssociated extends Error
{
public static $codes = array('card-already-funding-src');
}
class CannotAssociateCard extends Error
{
public static $codes = array('cannot-associate-card');
}
class BankAccountAlreadyAssociated extends Error
{
public static $codes = array('bank-account-already-associated');
}
class AddressVerificationFailed extends Error
{
public static $codes = array('address-verification-failed');
}
class HoldExpired extends Error
{
public static $codes = array('authorization-expired');
}
class MarketplaceAlreadyCreated extends Error
{
public static $codes = array('marketplace-already-created');
}
class IdentityVerificationFailed extends Error
{
public static $codes = array('identity-verification-error', 'business-principal-kyc', 'business-kyc', 'person-kyc');
}
class InsufficientFunds extends Error
{
public static $codes = array('insufficient-funds');
}
class CannotHold extends Error
{
public static $codes = array('funding-source-not-hold');
}
class CannotCredit extends Error
{
public static $codes = array('funding-destination-not-creditable');
}
class CannotDebit extends Error
{
public static $codes = array('funding-source-not-debitable');
}
class CannotRefund extends Error
{
public static $codes = array('funding-source-not-refundable');
}
class BankAccountVerificationFailure extends Error
{
public static $codes = array(
'bank-account-authentication-not-pending',
'bank-account-authentication-failed',
'bank-account-authentication-already-exists'
);
}

View file

@ -1,23 +0,0 @@
<?php
namespace Balanced;
use Balanced\Resource;
use \RESTful\URISpec;
/*
* An Event is a snapshot of another resource at a point in time when
* something significant occurred. Events are created when resources are
* created, updated, deleted or otherwise change state such as a Credit
* being marked as failed.
*/
class Event extends Resource
{
protected static $_uri_spec = null;
public static function init()
{
self::$_uri_spec = new URISpec('events', 'id', '/v1');
self::$_registry->add(get_called_class());
}
}

View file

@ -1,77 +0,0 @@
<?php
namespace Balanced;
use Balanced\Resource;
use \RESTful\URISpec;
/**
* Represents pending debit of funds for an account. The funds for that debit
* are held by the processor. You can later capture the hold, which results in
* debit, or void it, which releases the held funds.
*
* Note that a hold can expire so you should always check
* Balanced\Hold::expires_at.
*
* You create these using \Balanced\Account::hold.
*
* <code>
* $marketplace = \Balanced\Marketplace::mine();
*
* $account = $marketplace
* ->accounts
* ->query()
* ->filter(Account::f->email_address->eq('buyer@example.com'))
* ->one();
*
* $hold = $account->hold(
* 100,
* 'a description',
* null,
* array(
* 'my_id': '1293712837'
* )
* );
*
* $debit = $hold->capture();
* </code>
*/
class Hold extends Resource
{
protected static $_uri_spec = null;
public static function init()
{
self::$_uri_spec = new URISpec('holds', 'id');
self::$_registry->add(get_called_class());
}
/**
** Voids a pending hold. This releases the held funds. Once voided a hold
* is not longer pending can cannot be re-captured or re-voided.
*
* @return \Balanced\Hold
*/
public function void()
{
$this->is_void = true;
return $this->save();
}
/**
* Captures a pending hold. This results in a debit. Once captured a hold
* is not longer pending can cannot be re-captured or re-voided.
*
* @param int amount Optional Portion of the pending hold to capture. If not specified the full amount associated with the hold is captured.
*
* @return \Balanced\Debit
*/
public function capture($amount = null)
{
$this->debit = $this->account->debits->create(array(
'hold_uri' => $this->uri,
'amount' => $amount,
));
return $this->debit;
}
}

View file

@ -1,325 +0,0 @@
<?php
namespace Balanced;
use Balanced\Resource;
use Balanced\Errors;
use Balanced\Account;
use \RESTful\URISpec;
/**
* Represents a marketplace.
*
* To get started you create an api key and then create a marketplace:
*
* <code>
* $api_key = new \Balanced\APIKey();
* $api_key->save();
* $secret = $api_key->secret // better save this somewhere
* print $secret;
* \Balanced\Settings::$api_key = $secret;
*
* $marketplace = new \Balanced\Marketplace();
* $marketplace->save();
* var_dump($marketplace);
* </code>
*
* Each api key is uniquely associated with an api key so once you've created a
* marketplace:
*
* <code>
* \Balanced\Settings::$api_key = $secret;
* $marketplace = \Balanced\Marketplace::mine(); // this is the marketplace associated with $secret
* </code>
*/
class Marketplace extends Resource
{
protected static $_uri_spec = null;
public static function init()
{
self::$_uri_spec = new URISpec('marketplaces', 'id', '/v1');
self::$_registry->add(get_called_class());
}
/**
* Get the marketplace associated with the currently configured
* \Balanced\Settings::$api_key.
*
* @throws \RESTful\Exceptions\NoResultFound
* @return \Balanced\Marketplace
*/
public static function mine()
{
return self::query()->one();
}
/**
* Create a card. These can later be associated with an account using
* \Balanced\Account->addCard or \Balanced\Marketplace->createBuyer.
*
* @param string street_address Street address. Use null if there is no address for the card.
* @param string city City. Use null if there is no address for the card.
* @param string postal_code Postal code. Use null if there is no address for the card.
* @param string name Name as it appears on the card.
* @param string card_number Card number.
* @param string security_code Card security code. Use null if it is no available.
* @param int expiration_month Expiration month.
* @param int expiration_year Expiration year.
*
* @return \Balanced\Card
*/
public function createCard(
$street_address,
$city,
$region,
$postal_code,
$name,
$card_number,
$security_code,
$expiration_month,
$expiration_year)
{
if ($region != null && strlen($region) > 0) {
trigger_error("The region parameter will be deprecated in the next minor version of balanced-php", E_USER_NOTICE);
}
return $this->cards->create(array(
'street_address' => $street_address,
'city' => $city,
'region' => $region,
'postal_code' => $postal_code,
'name' => $name,
'card_number' => $card_number,
'security_code' => $security_code,
'expiration_month' => $expiration_month,
'expiration_year' => $expiration_year
));
}
/**
* Create a bank account. These can later be associated with an account
* using \Balanced\Account->addBankAccount.
*
* @param string name Name of the account holder.
* @param string account_number Account number.
* @param string routing_number Bank code or routing number.
* @param string type checking or savings
* @param array meta Single level mapping from string keys to string values.
*
* @return \Balanced\BankAccount
*/
public function createBankAccount(
$name,
$account_number,
$routing_number,
$type,
$meta = null
)
{
return $this->bank_accounts->create(array(
'name' => $name,
'account_number' => $account_number,
'routing_number' => $routing_number,
'type' => $type,
'meta' => $meta
));
}
/**
* Create a role-less account. You can later turn this into a buyer by
* adding a funding source (e.g a card) or a merchant using
* \Balanced\Account->promoteToMerchant.
*
* @param string email_address Optional email address. There can only be one account with this email address.
* @param array[string]string meta Optional metadata to associate with the account.
*
* @return \Balanced\Account
*/
public function createAccount($email_address = null, $meta = null)
{
return $this->accounts->create(array(
'email_address' => $email_address,
'meta' => $meta,
));
}
/**
* Find or create a role-less account by email address. You can later turn
* this into a buyer by adding a funding source (e.g a card) or a merchant
* using \Balanced\Account->promoteToMerchant.
*
* @param string email_address Email address. There can only be one account with this email address.
*
* @return \Balanced\Account
*/
function findOrCreateAccountByEmailAddress($email_address)
{
$marketplace = Marketplace::mine();
try {
$account = $this->accounts->create(array(
'email_address' => $email_address
));
}
catch (Errors\DuplicateAccountEmailAddress $e) {
$account = Account::get($e->extras->account_uri);
}
return $account;
}
/**
* Create a buyer account.
*
* @param string email_address Optional email address. There can only be one account with this email address.
* @param string card_uri URI referencing a card to associate with the account.
* @param array[string]string meta Optional metadata to associate with the account.
* @param string name Optional name of the account.
*
* @return \Balanced\Account
*/
public function createBuyer($email_address, $card_uri, $meta = null, $name = null)
{
return $this->accounts->create(array(
'email_address' => $email_address,
'card_uri' => $card_uri,
'meta' => $meta,
'name' => $name
));
}
/**
* Create a merchant account.
*
* Unlike buyers the identity of a merchant must be established before
* the account can function as a merchant (i.e. be credited). A merchant
* can be either a person or a business. Either way that information is
* represented as an associative array and passed as the merchant parameter
* when creating the merchant account.
*
* For a person the array looks like this:
*
* <code>
* array(
* 'type' => 'person',
* 'name' => 'William James',
* 'tax_id' => '393-48-3992',
* 'street_address' => '167 West 74th Street',
* 'postal_code' => '10023',
* 'dob' => '1842-01-01',
* 'phone_number' => '+16505551234',
* 'country_code' => 'USA'
* )
* </code>
*
* For a business the array looks like this:
*
* <code>
* array(
* 'type' => 'business',
* 'name' => 'Levain Bakery',
* 'tax_id' => '253912384',
* 'street_address' => '167 West 74th Street',
* 'postal_code' => '10023',
* 'phone_number' => '+16505551234',
* 'country_code' => 'USA',
* 'person' => array(
* 'name' => 'William James',
* 'tax_id' => '393483992',
* 'street_address' => '167 West 74th Street',
* 'postal_code' => '10023',
* 'dob' => '1842-01-01',
* 'phone_number' => '+16505551234',
* 'country_code' => 'USA',
* )
* )
* </code>
*
* In some cases the identity of the merchant, person or business, cannot
* be verified in which case a \Balanced\Exceptions\HTTPError is thrown:
*
* <code>
* $identity = array(
* 'type' => 'business',
* 'name' => 'Levain Bakery',
* 'tax_id' => '253912384',
* 'street_address' => '167 West 74th Street',
* 'postal_code' => '10023',
* 'phone_number' => '+16505551234',
* 'country_code' => 'USA',
* 'person' => array(
* 'name' => 'William James',
* 'tax_id' => '393483992',
* 'street_address' => '167 West 74th Street',
* 'postal_code' => '10023',
* 'dob' => '1842-01-01',
* 'phone_number' => '+16505551234',
* 'country_code' => 'USA',
* ),
* );
*
* try {
* $merchant = \Balanced\Marketplace::mine()->createMerchant(
* 'merchant@example.com',
* $identity,
* );
* catch (\Balanced\Exceptions\HTTPError $e) {
* if ($e->code != 300) {
* throw $e;
* }
* print e->response->header['Location'] // this is where merchant must signup
* }
* </code>
*
* Once the merchant has completed signup you can use the resulting URI to
* create an account for them on your marketplace:
*
* <code>
* $merchant = self::$marketplace->createMerchant(
* 'merchant@example.com',
* null,
* null,
* $merchant_uri
* );
* </coe>
*
* @param string email_address Optional email address. There can only be one account with this email address.
* @param array[string]mixed merchant Associative array describing the merchants identity.
* @param string $bank_account_uri Optional URI referencing a bank account to associate with this account.
* @param string $merchant_uri URI of a merchant created via the redirection sign-up flow.
* @param string $name Optional name of the merchant.
* @param array[string]string meta Optional metadata to associate with the account.
*
* @return \Balanced\Account
*/
public function createMerchant(
$email_address = null,
$merchant = null,
$bank_account_uri = null,
$merchant_uri = null,
$name = null,
$meta = null)
{
return $this->accounts->create(array(
'email_address' => $email_address,
'merchant' => $merchant,
'merchant_uri' => $merchant_uri,
'bank_account_uri' => $bank_account_uri,
'name' => $name,
'meta' => $meta,
));
}
/*
* Create a callback.
*
* @param string url URL of callback.
*/
public function createCallback(
$url
)
{
return $this->callbacks->create(array(
'url' => $url
));
}
}

View file

@ -1,51 +0,0 @@
<?php
namespace Balanced;
use Balanced\Resource;
use \RESTful\URISpec;
/**
* Represents a merchant identity.
*
* These are optionally created and associated with an account via
* \Balanced\Marketplace::createMerchant which establishes a merchant account
* on a marketplace.
*
* In some cases a merchant may need to be redirected to create a identity (e.g. the
* information provided cannot be verified, more information is needed, etc). That
* redirected signup results in a merchant_uri which is then associated with an
* account on the marketplace via \Balanced\Marketplace::createMerchant.
*
* @see \Balanced\Marketplace
*/
class Merchant extends Resource
{
protected static $_uri_spec = null;
public static function init()
{
self::$_uri_spec = new URISpec('merchants', 'id', '/v1');
self::$_registry->add(get_called_class());
}
/**
* Return the merchant identity associated with the current
* Balanced\Settings::$api_key. If you are not authenticated (i.e.
* ) then Balanced\Exceptions\NoResult
* will be thrown.
*
* <code>
* $merchant = \Balanced\Merchant::me();
* $owner_account = \Balanced\Marketplace::mine()->owner_account;
* assert($merchant->id == $owner_account->merchant->id);
* </code>
*
* @throws \RESTful\Exceptions\NoResultFound
* @return \Balanced\Merchant
*/
public static function me()
{
return self::query()->one();
}
}

View file

@ -1,50 +0,0 @@
<?php
namespace Balanced;
use Balanced\Resource;
use \RESTful\URISpec;
/**
* Represents a refund of an account debit transaction.
*
* You create these via Balanced\Debit::refund.
*
* <code>
* $marketplace = \Balanced\Marketplace::mine();
*
* $account = $marketplace
* ->accounts
* ->query()
* ->filter(Account::f->email_address->eq('buyer@example.com'))
* ->one();
*
* $debit = $account->debit(
* 100,
* 'how it appears on the statement',
* 'a description',
* array(
* 'my_id': '443322'
* )
* );
*
* $debit->refund(
* 99,
* 'some description',
* array(
* 'my_id': '123123'
* )
* );
* </code>
*/
class Refund extends Resource
{
protected static $_uri_spec = null;
public static function init()
{
self::$_uri_spec = new URISpec('refunds', 'id');
self::$_registry->add(get_called_class());
}
}

View file

@ -1,48 +0,0 @@
<?php
namespace Balanced;
use Balanced\Errors\Error;
use RESTful\Exceptions\HTTPError;
class Resource extends \RESTful\Resource
{
public static $fields, $f;
protected static $_client, $_registry, $_uri_spec;
public static function init()
{
self::$_client = new \RESTful\Client('\Balanced\Settings', null, __NAMESPACE__ .'\Resource::convertError');
self::$_registry = new \RESTful\Registry();
self::$f = self::$fields = new \RESTful\Fields();
}
public static function convertError($response)
{
if (property_exists($response->body, 'category_code') &&
array_key_exists($response->body->category_code, Error::$codes))
$error = new Error::$codes[$response->body->category_code]($response);
else
$error = new HTTPError($response);
return $error;
}
public static function getClient()
{
$class = get_called_class();
return $class::$_client;
}
public static function getRegistry()
{
$class = get_called_class();
return $class::$_registry;
}
public static function getURISpec()
{
$class = get_called_class();
return $class::$_uri_spec;
}
}

View file

@ -1,43 +0,0 @@
<?php
namespace Balanced;
/**
* Configurable settings.
*
* You can either set these settings individually:
*
* <code>
* \Balanced\Settngs::api_key = 'my-api-key-secret';
* </code>
*
* or all at once:
*
* <code>
* \Balanced\Settngs::configure(
* 'https://api.balancedpayments.com',
* 'my-api-key-secret'
* );
* </code>
*/
class Settings
{
const VERSION = '0.7.1';
public static $url_root = 'https://api.balancedpayments.com',
$api_key = null,
$agent = 'balanced-php',
$version = Settings::VERSION;
/**
* Configure all settings.
*
* @param string url_root The root (schema://hostname[:port]) to use when constructing api URLs.
* @param string api_key The api key secret to use for authenticating when talking to the api. If null then api usage is limited to uauthenticated endpoints.
*/
public static function configure($url_root, $api_key)
{
self::$url_root= $url_root;
self::$api_key = $api_key;
}
}

View file

@ -1,614 +0,0 @@
<?php
namespace Balanced\Test;
\Balanced\Bootstrap::init();
\RESTful\Bootstrap::init();
\Httpful\Bootstrap::init();
use Balanced\Resource;
use Balanced\Settings;
use Balanced\APIKey;
use Balanced\Marketplace;
use Balanced\Credit;
use Balanced\Debit;
use Balanced\Refund;
use Balanced\Account;
use Balanced\Merchant;
use Balanced\BankAccount;
use Balanced\Card;
use Balanced\Hold;
use \RESTful\Collection;
class APIKeyTest extends \PHPUnit_Framework_TestCase
{
function testRegistry()
{
$this->expectOutputString('');
$result = Resource::getRegistry()->match('/v1/api_keys');
return;
$expected = array(
'collection' => true,
'class' => 'Balanced\APIKey',
);
$this->assertEquals($expected, $result);
$result = Resource::getRegistry()->match('/v1/api_keys/1234');
$expected = array(
'collection' => false,
'class' => 'Balanced\APIKey',
'ids' => array('id' => '1234'),
);
$this->assertEquals($expected, $result);
}
}
class MarketplaceTest extends \PHPUnit_Framework_TestCase
{
function testRegistry()
{
$result = Resource::getRegistry()->match('/v1/marketplaces');
$expected = array(
'collection' => true,
'class' => 'Balanced\Marketplace',
);
$this->assertEquals($expected, $result);
$result = Resource::getRegistry()->match('/v1/marketplaces/1122');
$expected = array(
'collection' => false,
'class' => 'Balanced\Marketplace',
'ids' => array('id' => '1122'),
);
$this->assertEquals($expected, $result);
}
function testCreateCard()
{
$collection = $this->getMock(
'\RESTful\Collection',
array('create'),
array('\Balanced\Card', 'some/uri', null)
);
$collection->expects($this->once())
->method('create')
->with(array(
'street_address' => '123 Fake Street',
'city' => 'Jollywood',
'region' => '',
'postal_code' => '90210',
'name' => 'khalkhalash',
'card_number' => '4112344112344113',
'security_code' => '123',
'expiration_month' => 12,
'expiration_year' => 2013,
));
$marketplace = new Marketplace(array('cards' => $collection));
$marketplace->createCard(
'123 Fake Street',
'Jollywood',
'',
'90210',
'khalkhalash',
'4112344112344113',
'123',
12,
2013);
}
function testCreateBankAccount()
{
$collection = $this->getMock(
'\RESTful\Collection',
array('create'),
array('\Balanced\BankAccount', 'some/uri', null)
);
$collection->expects($this->once())
->method('create')
->with(array(
'name' => 'Homer Jay',
'account_number' => '112233a',
'routing_number' => '121042882',
'type' => 'savings',
'meta' => null
));
$marketplace = new Marketplace(array('bank_accounts' => $collection));
$marketplace->createBankAccount(
'Homer Jay',
'112233a',
'121042882',
'savings');
}
function testCreateAccount()
{
$collection = $this->getMock(
'\RESTful\Collection',
array('create'),
array('\Balanced\Account', 'some/uri', null)
);
$collection->expects($this->once())
->method('create')
->with(array(
'email_address' => 'role-less@example.com',
'meta' => array('test#' => 'test_d')
));
$marketplace = new Marketplace(array('accounts' => $collection));
$marketplace->createAccount(
'role-less@example.com',
array('test#' => 'test_d')
);
}
function testCreateBuyer()
{
$collection = $this->getMock(
'\RESTful\Collection',
array('create'),
array('\Balanced\Account', 'some/uri', null)
);
$collection->expects($this->once())
->method('create')
->with(array(
'email_address' => 'buyer@example.com',
'card_uri' => '/some/card/uri',
'meta' => array('test#' => 'test_d'),
'name' => 'Buy Er'
));
$marketplace = new Marketplace(array('accounts' => $collection));
$marketplace->createBuyer(
'buyer@example.com',
'/some/card/uri',
array('test#' => 'test_d'),
'Buy Er'
);
}
}
class AccountTest extends \PHPUnit_Framework_TestCase
{
function testRegistry()
{
$result = Resource::getRegistry()->match('/v1/accounts');
$expected = array(
'collection' => true,
'class' => 'Balanced\Account',
);
$this->assertEquals($expected, $result);
$result = Resource::getRegistry()->match('/v1/accounts/0099');
$expected = array(
'collection' => false,
'class' => 'Balanced\Account',
'ids' => array('id' => '0099'),
);
$this->assertEquals($expected, $result);
}
function testCredit()
{
$collection = $this->getMock(
'\RESTful\Collection',
array('create'),
array('\Balanced\Credit', 'some/uri', null)
);
$collection
->expects($this->once())
->method('create')
->with(array(
'amount' => 101,
'description' => 'something sweet',
'meta' => null,
'destination_uri' => null,
'appears_on_statement_as' => null
));
$account = new Account(array('credits' => $collection));
$account->credit(101, 'something sweet');
}
function testDebit()
{
$collection = $this->getMock(
'\RESTful\Collection',
array('create'),
array('\Balanced\Debit', 'some/uri', null)
);
$collection
->expects($this->once())
->method('create')
->with(array(
'amount' => 9911,
'description' => 'something tangy',
'appears_on_statement_as' => 'BAL*TANG',
'meta' => null,
'source_uri' => null,
'on_behalf_of_uri' => null,
));
$account = new Account(array('debits' => $collection));
$account->debit(9911, 'BAL*TANG', 'something tangy');
}
function testHold()
{
$collection = $this->getMock(
'\RESTful\Collection',
array('create'),
array('\Balanced\Hold', 'some/uri', null)
);
$collection
->expects($this->once())
->method('create')
->with(array(
'amount' => 1243,
'description' => 'something crispy',
'source_uri' => '/some/card/uri',
'meta' => array('test#' => 'test_d')
));
$account = new Account(array('holds' => $collection));
$account->hold(
1243,
'something crispy',
'/some/card/uri',
array('test#' => 'test_d')
);
}
function testAddCard()
{
$account = $this->getMock(
'\Balanced\Account',
array('save')
);
$account
->expects($this->once())
->method('save')
->with();
$account->addCard('/my/new/card/121212');
$this->assertEquals($account->card_uri, '/my/new/card/121212');
}
function testAddBankAccount()
{
$account = $this->getMock(
'\Balanced\Account',
array('save')
);
$account
->expects($this->once())
->method('save')
->with();
$account->addBankAccount('/my/new/bank_account/121212');
$this->assertEquals($account->bank_account_uri, '/my/new/bank_account/121212');
}
function testPromotToMerchant()
{
$account = $this->getMock(
'\Balanced\Account',
array('save')
);
$account
->expects($this->once())
->method('save')
->with();
$merchant = array(
'type' => 'person',
'name' => 'William James',
'tax_id' => '393-48-3992',
'street_address' => '167 West 74th Street',
'postal_code' => '10023',
'dob' => '1842-01-01',
'phone_number' => '+16505551234',
'country_code' => 'USA'
);
$account->promoteToMerchant($merchant);
$this->assertEquals($account->merchant, $merchant);
}
}
class HoldTest extends \PHPUnit_Framework_TestCase
{
function testRegistry()
{
$result = Resource::getRegistry()->match('/v1/holds');
$expected = array(
'collection' => true,
'class' => 'Balanced\Hold',
);
$this->assertEquals($expected, $result);
$result = Resource::getRegistry()->match('/v1/holds/112233');
$expected = array(
'collection' => false,
'class' => 'Balanced\Hold',
'ids' => array('id' => '112233'),
);
$this->assertEquals($expected, $result);
}
function testVoid()
{
$hold = $this->getMock(
'\Balanced\Hold',
array('save')
);
$hold
->expects($this->once())
->method('save')
->with();
$hold->void();
$this->assertTrue($hold->is_void);
}
function testCapture()
{
$collection = $this->getMock(
'\RESTful\Collection',
array('create'),
array('\Balanced\Debit', 'some/uri', null)
);
$collection
->expects($this->once())
->method('create')
->with(array(
'hold_uri' => 'some/hold/uri',
'amount' => 2211,
));
$account = new Account(array('debits' => $collection));
$hold = new Hold(array('uri' => 'some/hold/uri', 'account' => $account));
$hold->capture(2211);
}
}
class CreditTest extends \PHPUnit_Framework_TestCase
{
function testRegistry()
{
$result = Resource::getRegistry()->match('/v1/credits');
$expected = array(
'collection' => true,
'class' => 'Balanced\Credit',
);
$this->assertEquals($expected, $result);
$result = Resource::getRegistry()->match('/v1/credits/9988');
$expected = array(
'collection' => false,
'class' => 'Balanced\Credit',
'ids' => array('id' => '9988'),
);
$this->assertEquals($expected, $result);
}
}
class DebitTest extends \PHPUnit_Framework_TestCase
{
function testRegistry()
{
$result = Resource::getRegistry()->match('/v1/debits');
$expected = array(
'collection' => true,
'class' => 'Balanced\Debit',
);
$this->assertEquals($expected, $result);
$result = Resource::getRegistry()->match('/v1/debits/4545');
$expected = array(
'collection' => false,
'class' => 'Balanced\Debit',
'ids' => array('id' => '4545'),
);
$this->assertEquals($expected, $result);
}
function testRefund()
{
$collection = $this->getMock(
'\RESTful\Collection',
array('create'),
array('\Balanced\Refund', 'some/uri', null)
);
$collection
->expects($this->once())
->method('create')
->with(array(
'amount' => 5645,
'description' => null,
'meta' => array('test#' => 'test_d')
));
$debit = new Debit(array('refunds' => $collection));
$debit->refund(5645, null, array('test#' => 'test_d'));
}
}
class RefundTest extends \PHPUnit_Framework_TestCase
{
function testRegistry()
{
$result = Resource::getRegistry()->match('/v1/refunds');
$expected = array(
'collection' => true,
'class' => 'Balanced\Refund',
);
$this->assertEquals($expected, $result);
$result = Resource::getRegistry()->match('/v1/refunds/1287');
$expected = array(
'collection' => false,
'class' => 'Balanced\Refund',
'ids' => array('id' => '1287'),
);
$this->assertEquals($expected, $result);
}
}
class BankAccountTest extends \PHPUnit_Framework_TestCase
{
function testRegistry()
{
$result = Resource::getRegistry()->match('/v1/bank_accounts');
$expected = array(
'collection' => true,
'class' => 'Balanced\BankAccount',
);
$this->assertEquals($expected, $result);
$result = Resource::getRegistry()->match('/v1/bank_accounts/887766');
$expected = array(
'collection' => false,
'class' => 'Balanced\BankAccount',
'ids' => array('id' => '887766'),
);
$this->assertEquals($expected, $result);
}
function testCreditAccount()
{
$collection = $this->getMock(
'\RESTful\Collection',
array('create'),
array('\Balanced\Credit', 'some/uri', null)
);
$collection
->expects($this->once())
->method('create')
->with(array(
'amount' => 101,
'description' => 'something super sweet',
'meta' => null,
'destination_uri' => '/some/other/uri',
'appears_on_statement_as' => null
));
$account = new Account(array('credits' => $collection));
$bank_account = new BankAccount(array('uri' => '/some/other/uri', 'account' => $account));
$bank_account->credit(101, 'something super sweet');
}
function testCreditAccountless()
{
$collection = $this->getMock(
'\RESTful\Collection',
array('create'),
array('\Balanced\Credit', 'some/uri', null)
);
$collection
->expects($this->once())
->method('create')
->with(array(
'amount' => 101,
'description' => 'something super sweet',
));
$bank_account = new BankAccount(array(
'uri' => '/some/other/uri',
'account' => null,
'credits' => $collection,
));
$bank_account->credit(101, 'something super sweet');
}
}
class CardTest extends \PHPUnit_Framework_TestCase
{
function testRegistry()
{
$result = Resource::getRegistry()->match('/v1/cards');
$expected = array(
'collection' => true,
'class' => 'Balanced\Card',
);
$this->assertEquals($expected, $result);
$result = Resource::getRegistry()->match('/v1/cards/136asd6713');
$expected = array(
'collection' => false,
'class' => 'Balanced\Card',
'ids' => array('id' => '136asd6713'),
);
$this->assertEquals($expected, $result);
}
function testDebit()
{
$collection = $this->getMock(
'\RESTful\Collection',
array('create'),
array('\Balanced\Debit', 'some/uri', null)
);
$account = new Account(array('debits' => $collection));
$card = new Card(array('uri' => '/some/uri', 'account' => $account ));
$collection
->expects($this->once())
->method('create')
->with(array(
'amount' => 9911,
'description' => 'something tangy',
'appears_on_statement_as' => 'BAL*TANG',
'meta' => null,
'source_uri' => '/some/uri',
'on_behalf_of_uri' => null,
));
$card->debit(9911, 'BAL*TANG', 'something tangy');
}
/**
* @expectedException \UnexpectedValueException
*/
function testNotAssociatedDebit()
{
$card = new Card(array('uri' => '/some/uri', 'account' => null ));
$card->debit(9911, 'BAL*TANG', 'something tangy');
}
}
class MerchantTest extends \PHPUnit_Framework_TestCase
{
function testRegistry()
{
$result = Resource::getRegistry()->match('/v1/merchants');
$expected = array(
'collection' => true,
'class' => 'Balanced\Merchant',
);
$this->assertEquals($expected, $result);
$result = Resource::getRegistry()->match('/v1/merchants/136asd6713');
$expected = array(
'collection' => false,
'class' => 'Balanced\Merchant',
'ids' => array('id' => '136asd6713'),
);
$this->assertEquals($expected, $result);
}
}

View file

@ -1,798 +0,0 @@
<?php
namespace Balanced\Test;
\Balanced\Bootstrap::init();
\RESTful\Bootstrap::init();
\Httpful\Bootstrap::init();
use Balanced\Settings;
use Balanced\APIKey;
use Balanced\Marketplace;
use Balanced\Credit;
use Balanced\Debit;
use Balanced\Refund;
use Balanced\Account;
use Balanced\Merchant;
use Balanced\BankAccount;
use Balanced\Card;
/**
* Suite test cases. These talk to an API server and so make network calls.
*
* Environment variables can be used to control client settings:
*
* <ul>
* <li>$BALANCED_URL_ROOT If set applies to \Balanced\Settings::$url_root.
* <li>$BALANCED_API_KEY If set applies to \Balanced\Settings::$api_key.
* </ul>
*/
class SuiteTest extends \PHPUnit_Framework_TestCase
{
static $key,
$marketplace,
$email_counter = 0;
static function _createBuyer($email_address = null, $card = null)
{
if ($email_address == null)
$email_address = sprintf('m+%d@poundpay.com', self::$email_counter++);
if ($card == null)
$card = self::_createCard();
return self::$marketplace->createBuyer(
$email_address,
$card->uri,
array('test#' => 'test_d'),
'Hobo Joe'
);
}
static function _createCard($account = null)
{
$card = self::$marketplace->createCard(
'123 Fake Street',
'Jollywood',
null,
'90210',
'khalkhalash',
'4112344112344113',
null,
12,
2013);
if ($account != null) {
$account->addCard($card);
$card = Card::get($card->uri);
}
return $card;
}
static function _createBankAccount($account = null)
{
$bank_account = self::$marketplace->createBankAccount(
'Homer Jay',
'112233a',
'121042882',
'checking'
);
if ($account != null) {
$account->addBankAccount($bank_account);
$bank_account = $account->bank_accounts[0];
}
return $bank_account;
}
public static function _createPersonMerchant($email_address = null, $bank_account = null)
{
if ($email_address == null)
$email_address = sprintf('m+%d@poundpay.com', self::$email_counter++);
if ($bank_account == null)
$bank_account = self::_createBankAccount();
$merchant = array(
'type' => 'person',
'name' => 'William James',
'tax_id' => '393-48-3992',
'street_address' => '167 West 74th Street',
'postal_code' => '10023',
'dob' => '1842-01-01',
'phone_number' => '+16505551234',
'country_code' => 'USA'
);
return self::$marketplace->createMerchant(
$email_address,
$merchant,
$bank_account->uri
);
}
public static function _createBusinessMerchant($email_address = null, $bank_account = null)
{
if ($email_address == null)
$email_address = sprintf('m+%d@poundpay.com', self::$email_counter++);
if ($bank_account == null)
$bank_account = self::_createBankAccount();
$merchant = array(
'type' => 'business',
'name' => 'Levain Bakery',
'tax_id' => '253912384',
'street_address' => '167 West 74th Street',
'postal_code' => '10023',
'phone_number' => '+16505551234',
'country_code' => 'USA',
'person' => array(
'name' => 'William James',
'tax_id' => '393483992',
'street_address' => '167 West 74th Street',
'postal_code' => '10023',
'dob' => '1842-01-01',
'phone_number' => '+16505551234',
'country_code' => 'USA',
),
);
return self::$marketplace->createMerchant(
$email_address,
$merchant,
$bank_account->uri
);
}
public static function setUpBeforeClass()
{
// url root
$url_root = getenv('BALANCED_URL_ROOT');
if ($url_root != '') {
Settings::$url_root = $url_root;
}
else
Settings::$url_root = 'https://api.balancedpayments.com';
// api key
$api_key = getenv('BALANCED_API_KEY');
if ($api_key != '') {
Settings::$api_key = $api_key;
}
else {
self::$key = new APIKey();
self::$key->save();
Settings::$api_key = self::$key->secret;
}
// marketplace
try {
self::$marketplace = Marketplace::mine();
}
catch(\RESTful\Exceptions\NoResultFound $e) {
self::$marketplace = new Marketplace();
self::$marketplace->save();
}
}
function testMarketplaceMine()
{
$marketplace = Marketplace::mine();
$this->assertEquals($this::$marketplace->id, $marketplace->id);
}
/**
* @expectedException \RESTful\Exceptions\HTTPError
*/
function testAnotherMarketplace()
{
$marketplace = new Marketplace();
$marketplace->save();
}
/**
* @expectedException \RESTful\Exceptions\HTTPError
*/
function testDuplicateEmailAddress()
{
self::_createBuyer('dupe@poundpay.com');
self::_createBuyer('dupe@poundpay.com');
}
function testIndexMarketplace()
{
$marketplaces = Marketplace::query()->all();
$this->assertEquals(count($marketplaces), 1);
}
function testCreateBuyer()
{
self::_createBuyer();
}
function testCreateAccountWithoutEmailAddress()
{
self::$marketplace->createAccount();
}
function testFindOrCreateAccountByEmailAddress()
{
$account1 = self::$marketplace->createAccount('foc@example.com');
$account2 = self::$marketplace->findOrCreateAccountByEmailAddress('foc@example.com');
$this->assertEquals($account2->id, $account2->id);
$account3 = self::$marketplace->findOrCreateAccountByEmailAddress('foc2@example.com');
$this->assertNotEquals($account3->id, $account1->id);
}
function testGetBuyer()
{
$buyer1 = self::_createBuyer();
$buyer2 = Account::get($buyer1->uri);
$this->assertEquals($buyer1->id, $buyer2->id);
}
function testMe()
{
$marketplace = Marketplace::mine();
$merchant = Merchant::me();
$this->assertEquals($marketplace->id, $merchant->marketplace->id);
}
function testDebitAndRefundBuyer()
{
$buyer = self::_createBuyer();
$debit = $buyer->debit(
1000,
'Softie',
'something i bought',
array('hi' => 'bye')
);
$refund = $debit->refund(100);
}
/**
* @expectedException \RESTful\Exceptions\HTTPError
*/
function testDebitZero()
{
$buyer = self::_createBuyer();
$debit = $buyer->debit(
0,
'Softie',
'something i bought'
);
}
function testMultipleRefunds()
{
$buyer = self::_createBuyer();
$debit = $buyer->debit(
1500,
'Softie',
'something tart',
array('hi' => 'bye'));
$refunds = array(
$debit->refund(100),
$debit->refund(100),
$debit->refund(100),
$debit->refund(100));
$expected_refund_ids = array_map(
function($x) {
return $x->id;
}, $refunds);
sort($expected_refund_ids);
$this->assertEquals($debit->refunds->total(), 4);
// itemization
$total = 0;
$refund_ids = array();
foreach ($debit->refunds as $refund) {
$total += $refund->amount;
array_push($refund_ids, $refund->id);
}
sort($refund_ids);
$this->assertEquals($total, 400);
$this->assertEquals($expected_refund_ids, $refund_ids);
// pagination
$total = 0;
$refund_ids = array();
foreach ($debit->refunds->paginate() as $page) {
foreach ($page->items as $refund) {
$total += $refund->amount;
array_push($refund_ids, $refund->id);
}
}
sort($refund_ids);
$this->assertEquals($total, 400);
$this->assertEquals($expected_refund_ids, $refund_ids);
}
function testDebitSource()
{
$buyer = self::_createBuyer();
$card1 = self::_createCard($buyer);
$card2 = self::_createCard($buyer);
$credit = $buyer->debit(
1000,
'Softie',
'something i bought'
);
$this->assertEquals($credit->source->id, $card2->id);
$credit = $buyer->debit(
1000,
'Softie',
'something i bought',
null,
$card1
);
$this->assertEquals($credit->source->id, $card1->id);
}
function testDebitOnBehalfOf()
{
$buyer = self::_createBuyer();
$merchant = self::$marketplace->createAccount(null);
$card1 = self::_createCard($buyer);
$debit = $buyer->debit(1000, null, null, null, null, $merchant);
$this->assertEquals($debit->amount, 1000);
// for now just test the debit succeeds.
// TODO: once the on_behalf_of actually shows up on the response, test it.
}
/**
* @expectedException \InvalidArgumentException
*/
function testDebitOnBehalfOfFailsForBuyer()
{
$buyer = self::_createBuyer();
$card1 = self::_createCard($buyer);
$debit = $buyer->debit(1000, null, null, null, null, $buyer);
}
function testCreateAndVoidHold()
{
$buyer = self::_createBuyer();
$hold = $buyer->hold(1000);
$this->assertEquals($hold->is_void, false);
$hold->void();
$this->assertEquals($hold->is_void, true);
}
function testCreateAndCaptureHold()
{
$buyer = self::_createBuyer();
$hold = $buyer->hold(1000);
$debit = $hold->capture(909);
$this->assertEquals($debit->account->id, $buyer->id);
$this->assertEquals($debit->hold->id, $hold->id);
$this->assertEquals($hold->debit->id, $debit->id);
}
function testCreatePersonMerchant()
{
$merchant = self::_createPersonMerchant();
}
function testCreateBusinessMerchant()
{
$merchant = self::_createBusinessMerchant();
}
/**
* @expectedException \RESTful\Exceptions\HTTPError
*/
function testCreditRequiresNonZeroAmount()
{
$buyer = self::_createBuyer();
$buyer->debit(
1000,
'Softie',
'something i bought'
);
$merchant = self::_createBusinessMerchant();
$merchant->credit(0);
}
/**
* @expectedException \RESTful\Exceptions\HTTPError
*/
function testCreditMoreThanEscrowBalanceFails()
{
$buyer = self::_createBuyer();
$buyer->credit(
1000,
'something i bought',
null,
null,
'Softie'
);
$merchant = self::_createBusinessMerchant();
$merchant->credit(self::$marketplace->in_escrow + 1);
}
function testCreditDestiation()
{
$buyer = self::_createBuyer();
$buyer->debit(3000); # NOTE: build up escrow balance to credit
$merchant = self::_createPersonMerchant();
$bank_account1 = self::_createBankAccount($merchant);
$bank_account2 = self::_createBankAccount($merchant);
$credit = $merchant->credit(
1000,
'something i sold',
null,
null,
'Softie'
);
$this->assertEquals($credit->destination->id, $bank_account2->id);
$credit = $merchant->credit(
1000,
'something i sold',
null,
$bank_account1,
'Softie'
);
$this->assertEquals($credit->destination->id, $bank_account1->id);
}
function testAssociateCard()
{
$merchant = self::_createPersonMerchant();
$card = self::_createCard();
$merchant->addCard($card->uri);
}
function testAssociateBankAccount()
{
$merchant = self::_createPersonMerchant();
$bank_account = self::_createBankAccount();
$merchant->addBankAccount($bank_account->uri);
}
function testCardMasking()
{
$card = self::$marketplace->createCard(
'123 Fake Street',
'Jollywood',
null,
'90210',
'khalkhalash',
'4112344112344113',
'123',
12,
2013);
$this->assertEquals($card->last_four, '4113');
$this->assertFalse(property_exists($card, 'number'));
}
function testBankAccountMasking()
{
$bank_account = self::$marketplace->createBankAccount(
'Homer Jay',
'112233a',
'121042882',
'checking'
);
$this->assertEquals($bank_account->last_four, '233a');
$this->assertEquals($bank_account->account_number, 'xxx233a');
}
function testFilteringAndSorting()
{
$buyer = self::_createBuyer();
$debit1 = $buyer->debit(1122, null, null, array('tag' => '1'));
$debit2 = $buyer->debit(3322, null, null, array('tag' => '1'));
$debit3 = $buyer->debit(2211, null, null, array('tag' => '2'));
$getId = function($o) {
return $o->id;
};
$debits = (
self::$marketplace->debits->query()
->filter(Debit::$f->meta->tag->eq('1'))
->sort(Debit::$f->created_at->asc())
->all());
$debit_ids = array_map($getId, $debits);
$this->assertEquals($debit_ids, array($debit1->id, $debit2->id));
$debits = (
self::$marketplace->debits->query()
->filter(Debit::$f->meta->tag->eq('2'))
->all());
$debit_ids = array_map($getId, $debits);
$this->assertEquals($debit_ids, array($debit3->id));
$debits = (
self::$marketplace->debits->query()
->filter(Debit::$f->meta->contains('tag'))
->sort(Debit::$f->created_at->asc())
->all());
$debit_ids = array_map($getId, $debits);
$this->assertEquals($debit_ids, array($debit1->id, $debit2->id, $debit3->id));
$debits = (
self::$marketplace->debits->query()
->filter(Debit::$f->meta->contains('tag'))
->sort(Debit::$f->amount->desc())
->all());
$debit_ids = array_map($getId, $debits);
$this->assertEquals($debit_ids, array($debit2->id, $debit3->id, $debit1->id));
}
function testMerchantIdentityFailure()
{
// NOTE: postal_code == '99999' && region == 'EX' triggers identity failure
$identity = array(
'type' => 'business',
'name' => 'Levain Bakery',
'tax_id' => '253912384',
'street_address' => '167 West 74th Street',
'postal_code' => '99999',
'region' => 'EX',
'phone_number' => '+16505551234',
'country_code' => 'USA',
'person' => array(
'name' => 'William James',
'tax_id' => '393483992',
'street_address' => '167 West 74th Street',
'postal_code' => '99999',
'region' => 'EX',
'dob' => '1842-01-01',
'phone_number' => '+16505551234',
'country_code' => 'USA',
),
);
try {
self::$marketplace->createMerchant(
sprintf('m+%d@poundpay.com', self::$email_counter++),
$identity);
}
catch(\RESTful\Exceptions\HTTPError $e) {
$this->assertEquals($e->response->code, 300);
$expected = sprintf('https://www.balancedpayments.com/marketplaces/%s/kyc', self::$marketplace->id);
$this->assertEquals($e->redirect_uri, $expected);
$this->assertEquals($e->response->headers['Location'], $expected);
return;
}
$this->fail('Expected exception HTTPError not raised.');
}
function testInternationalCard()
{
$payload = array(
'card_number' => '4111111111111111',
'city' => '\xe9\x83\xbd\xe7\x95\x99\xe5\xb8\x82',
'country_code' => 'JPN',
'expiration_month' => 12,
'expiration_year' => 2014,
'name' => 'Johnny Fresh',
'postal_code' => '4020054',
'street_address' => '\xe7\x94\xb0\xe5\x8e\x9f\xef\xbc\x93\xe3\x83\xbc\xef\xbc\x98\xe3\x83\xbc\xef\xbc\x91'
);
$card = self::$marketplace->cards->create($payload);
$this->assertEquals($card->street_address, $payload['street_address']);
}
/**
* @expectedException \RESTful\Exceptions\NoResultFound
*/
function testAccountWithEmailAddressNotFound()
{
self::$marketplace->accounts->query()
->filter(Account::$f->email_address->eq('unlikely@address.com'))
->one();
}
function testDebitACard()
{
$buyer = self::_createBuyer();
$card = self::_createCard($buyer);
$debit = $card->debit(
1000,
'Softie',
'something i bought',
array('hi' => 'bye'));
$this->assertEquals($debit->source->uri, $card->uri);
}
/**
* @expectedException \UnexpectedValueException
*/
function testDebitAnUnassociatedCard()
{
$card = self::_createCard();
$card->debit(1000, 'Softie');
}
function testCreditABankAccount()
{
$buyer = self::_createBuyer();
$buyer->debit(101); # NOTE: build up escrow balance to credit
$merchant = self::_createPersonMerchant();
$bank_account = self::_createBankAccount($merchant);
$credit = $bank_account->credit(55, 'something sour');
$this->assertEquals($credit->destination->uri, $bank_account->uri);
}
function testQuery()
{
$buyer = self::_createBuyer();
$tag = '123123123123';
$debit1 = $buyer->debit(1122, null, null, array('tag' => $tag));
$debit2 = $buyer->debit(3322, null, null, array('tag' => $tag));
$debit3 = $buyer->debit(2211, null, null, array('tag' => $tag));
$expected_debit_ids = array($debit1->id, $debit2->id, $debit3->id);
$query = (
self::$marketplace->debits->query()
->filter(Debit::$f->meta->tag->eq($tag))
->sort(Debit::$f->created_at->asc())
->limit(1));
$this->assertEquals($query->total(), 3);
$debit_ids = array();
foreach ($query as $debits) {
array_push($debit_ids, $debits->id);
}
$this->assertEquals($debit_ids, $expected_debit_ids);
$debit_ids = array($query[0]->id, $query[1]->id, $query[2]->id);
$this->assertEquals($debit_ids, $expected_debit_ids);
}
function testBuyerPromoteToMerchant()
{
$merchant = array(
'type' => 'person',
'name' => 'William James',
'tax_id' => '393-48-3992',
'street_address' => '167 West 74th Street',
'postal_code' => '10023',
'dob' => '1842-01-01',
'phone_number' => '+16505551234',
'country_code' => 'USA'
);
$buyer = self::_createBuyer();
$buyer->promoteToMerchant($merchant);
}
function testCreditAccountlessBankAccount()
{
$buyer = self::_createBuyer();
$buyer->debit(101); # NOTE: build up escrow balance to credit
$bank_account = self::_createBankAccount();
$credit = $bank_account->credit(55, 'something sour');
$this->assertEquals($credit->bank_account->id, $bank_account->id);
$bank_account = $bank_account->get($bank_account->id);
$this->assertEquals($bank_account->credits->total(), 1);
}
function testCreditUnstoredBankAccount()
{
$buyer = self::_createBuyer();
$buyer->debit(101); # NOTE: build up escrow balance to credit
$credit = Credit::bankAccount(
55,
array(
'name' => 'Homer Jay',
'account_number' => '112233a',
'routing_number' => '121042882',
'type' => 'checking',
),
'something sour');
$this->assertFalse(property_exists($credit->bank_account, 'uri'));
$this->assertFalse(property_exists($credit->bank_account, 'id'));
$this->assertEquals($credit->bank_account->name, 'Homer Jay');
$this->assertEquals($credit->bank_account->account_number, 'xxx233a');
$this->assertEquals($credit->bank_account->type, 'checking');
}
function testDeleteBankAccount()
{
$buyer = self::_createBuyer();
$buyer->debit(101); # NOTE: build up escrow balance to credit
$bank_account = self::_createBankAccount();
$credit = $bank_account->credit(55, 'something sour');
$this->assertTrue(property_exists($credit->bank_account, 'uri'));
$this->assertTrue(property_exists($credit->bank_account, 'id'));
$bank_account = BankAccount::get($bank_account->id);
$bank_account->delete();
$credit = Credit::get($credit->uri);
$this->assertFalse(property_exists($credit->bank_account, 'uri'));
$this->assertFalse(property_exists($credit->bank_account, 'id'));
}
function testGetBankAccounById()
{
$bank_account = self::_createBankAccount();
$bank_account_2 = BankAccount::get($bank_account->id);
$this->assertEquals($bank_account_2->id, $bank_account->id);
}
/**
* @expectedException \Balanced\Errors\InsufficientFunds
*/
function testInsufficientFunds()
{
$marketplace = Marketplace::get(self::$marketplace->uri);
$amount = $marketplace->in_escrow + 100;
$credit = Credit::bankAccount(
$amount,
array(
'name' => 'Homer Jay',
'account_number' => '112233a',
'routing_number' => '121042882',
'type' => 'checking',
),
'something sour');
}
function testCreateCallback() {
$callback = self::$marketplace->createCallback(
'http://example.com/php'
);
$this->assertEquals($callback->url, 'http://example.com/php');
}
/**
* @expectedException \Balanced\Errors\BankAccountVerificationFailure
*/
function testBankAccountVerificationFailure() {
$bank_account = self::_createBankAccount();
$buyer = self::_createBuyer();
$buyer->addBankAccount($bank_account);
$verification = $bank_account->verify();
$verification->confirm(1, 2);
}
/**
* @expectedException \Balanced\Errors\BankAccountVerificationFailure
*/
function testBankAccountVerificationDuplicate() {
$bank_account = self::_createBankAccount();
$buyer = self::_createBuyer();
$buyer->addBankAccount($bank_account);
$bank_account->verify();
$bank_account->verify();
}
function testBankAccountVerificationSuccess() {
$bank_account = self::_createBankAccount();
$buyer = self::_createBuyer();
$buyer->addBankAccount($bank_account);
$verification = $bank_account->verify();
$verification->confirm(1, 1);
// this will fail if the bank account is not verified
$debit = $buyer->debit(
1000,
'Softie',
'something i bought',
array('hi' => 'bye'),
$bank_account
);
$this->assertTrue(strpos($debit->source->uri, 'bank_account') > 0);
}
function testEvents() {
$prev_num_events = Marketplace::mine()->events->total();
$account = self::_createBuyer();
$account->debit(123);
$cur_num_events = Marketplace::mine()->events->total();
$count = 0;
while ($cur_num_events == $prev_num_events && $count < 10) {
printf("waiting for events - %d, %d == %d\n", $count + 1, $cur_num_events, $prev_num_events);
sleep(2); // 2 seconds
$cur_num_events = Marketplace::mine()->events->total();
$count += 1;
}
$this->assertTrue($cur_num_events > $prev_num_events);
}
}

View file

@ -1,8 +0,0 @@
<phpunit>
<testsuite name="Balanced">
<directory>.</directory>
</testsuite>
<logging>
<log type="coverage-text" target="php://stdout" showUncoveredFiles="false"/>
</logging>
</phpunit>

View file

@ -44,7 +44,7 @@ return array(
'rsrc/css/application/config/config-welcome.css' => '6abd79be',
'rsrc/css/application/config/setup-issue.css' => '22270af2',
'rsrc/css/application/config/unhandled-exception.css' => '37d4f9a2',
'rsrc/css/application/conpherence/durable-column.css' => '8c951609',
'rsrc/css/application/conpherence/durable-column.css' => '1f5c64e8',
'rsrc/css/application/conpherence/menu.css' => 'c6ac5299',
'rsrc/css/application/conpherence/message-pane.css' => '5930260a',
'rsrc/css/application/conpherence/notification.css' => '04a6e10a',
@ -404,7 +404,6 @@ return array(
'rsrc/js/application/phame/phame-post-preview.js' => 'be807912',
'rsrc/js/application/pholio/behavior-pholio-mock-edit.js' => '9c2623f4',
'rsrc/js/application/pholio/behavior-pholio-mock-view.js' => 'e58bf807',
'rsrc/js/application/phortune/behavior-balanced-payment-form.js' => 'b2c03e60',
'rsrc/js/application/phortune/behavior-stripe-payment-form.js' => '3f5d6dbf',
'rsrc/js/application/phortune/behavior-test-payment-form.js' => 'fc91ab6c',
'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef',
@ -514,7 +513,7 @@ return array(
'changeset-view-manager' => '88be0133',
'config-options-css' => '7fedf08b',
'config-welcome-css' => '6abd79be',
'conpherence-durable-column-view' => '8c951609',
'conpherence-durable-column-view' => '1f5c64e8',
'conpherence-menu-css' => 'c6ac5299',
'conpherence-message-pane-css' => '5930260a',
'conpherence-notification-css' => '04a6e10a',
@ -554,7 +553,6 @@ return array(
'javelin-behavior-aphront-more' => 'a80d0378',
'javelin-behavior-audio-source' => '59b251eb',
'javelin-behavior-audit-preview' => 'd835b03a',
'javelin-behavior-balanced-payment-form' => 'b2c03e60',
'javelin-behavior-boards-dropdown' => '0ec56e1d',
'javelin-behavior-choose-control' => '6153c708',
'javelin-behavior-config-reorder-fields' => '14a827de',
@ -1670,11 +1668,6 @@ return array(
'javelin-uri',
'javelin-request',
),
'b2c03e60' => array(
'javelin-behavior',
'javelin-dom',
'phortune-credit-card-form',
),
'b3a4b884' => array(
'javelin-behavior',
'phabricator-prefab',

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

View file

@ -2779,7 +2779,6 @@ phutil_register_library_map(array(
'PhortuneAccountTransaction' => 'applications/phortune/storage/PhortuneAccountTransaction.php',
'PhortuneAccountTransactionQuery' => 'applications/phortune/query/PhortuneAccountTransactionQuery.php',
'PhortuneAccountViewController' => 'applications/phortune/controller/PhortuneAccountViewController.php',
'PhortuneBalancedPaymentProvider' => 'applications/phortune/provider/PhortuneBalancedPaymentProvider.php',
'PhortuneCart' => 'applications/phortune/storage/PhortuneCart.php',
'PhortuneCartAcceptController' => 'applications/phortune/controller/PhortuneCartAcceptController.php',
'PhortuneCartCancelController' => 'applications/phortune/controller/PhortuneCartCancelController.php',
@ -6176,7 +6175,6 @@ phutil_register_library_map(array(
'PhortuneAccountTransaction' => 'PhabricatorApplicationTransaction',
'PhortuneAccountTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhortuneAccountViewController' => 'PhortuneController',
'PhortuneBalancedPaymentProvider' => 'PhortunePaymentProvider',
'PhortuneCart' => array(
'PhortuneDAO',
'PhabricatorApplicationTransactionInterface',

View file

@ -1,370 +0,0 @@
<?php
final class PhortuneBalancedPaymentProvider extends PhortunePaymentProvider {
const BALANCED_MARKETPLACE_ID = 'balanced.marketplace-id';
const BALANCED_SECRET_KEY = 'balanced.secret-key';
public function isAcceptingLivePayments() {
return !preg_match('/-test-/', $this->getSecretKey());
}
public function getName() {
return pht('Balanced Payments');
}
public function getConfigureName() {
return pht('Add Balanced Payments Account');
}
public function getConfigureDescription() {
return pht(
'Allows you to accept credit or debit card payments with a '.
'balancedpayments.com account.');
}
public function getConfigureProvidesDescription() {
return pht(
'This merchant accepts credit and debit cards via Balanced Payments.');
}
public function getConfigureInstructions() {
return pht(
"To configure Balacned, register or log in to an existing account on ".
"[[https://balancedpayments.com | balancedpayments.com]]. Once logged ".
"in:\n\n".
" - Choose a marketplace.\n".
" - Find the **Marketplace ID** in {nav My Marketplace > Settings} and ".
" copy it into the field above.\n".
" - On the same screen, under **API keys**, choose **Add a key**, then ".
" **Show key secret**. Copy the value into the field above.\n\n".
"You can either use a test marketplace to add this provider in test ".
"mode, or use a live marketplace to accept live payments.");
}
public function getAllConfigurableProperties() {
return array(
self::BALANCED_MARKETPLACE_ID,
self::BALANCED_SECRET_KEY,
);
}
public function getAllConfigurableSecretProperties() {
return array(
self::BALANCED_SECRET_KEY,
);
}
public function processEditForm(
AphrontRequest $request,
array $values) {
$errors = array();
$issues = array();
if (!strlen($values[self::BALANCED_MARKETPLACE_ID])) {
$errors[] = pht('Balanced Marketplace ID is required.');
$issues[self::BALANCED_MARKETPLACE_ID] = pht('Required');
}
if (!strlen($values[self::BALANCED_SECRET_KEY])) {
$errors[] = pht('Balanced Secret Key is required.');
$issues[self::BALANCED_SECRET_KEY] = pht('Required');
}
return array($errors, $issues, $values);
}
public function extendEditForm(
AphrontRequest $request,
AphrontFormView $form,
array $values,
array $issues) {
$form
->appendChild(
id(new AphrontFormTextControl())
->setName(self::BALANCED_MARKETPLACE_ID)
->setValue($values[self::BALANCED_MARKETPLACE_ID])
->setError(idx($issues, self::BALANCED_MARKETPLACE_ID, true))
->setLabel(pht('Balanced Marketplace ID')))
->appendChild(
id(new AphrontFormTextControl())
->setName(self::BALANCED_SECRET_KEY)
->setValue($values[self::BALANCED_SECRET_KEY])
->setError(idx($issues, self::BALANCED_SECRET_KEY, true))
->setLabel(pht('Balanced Secret Key')));
}
public function canRunConfigurationTest() {
return true;
}
public function runConfigurationTest() {
$this->loadBalancedAPILibraries();
// TODO: This only tests that the secret key is correct. It's not clear
// how to test that the marketplace is correct.
try {
Balanced\Settings::$api_key = $this->getSecretKey();
Balanced\APIKey::query()->first();
} catch (RESTful\Exceptions\HTTPError $error) {
// NOTE: This exception doesn't print anything meaningful if it escapes
// to top level. Replace it with something slightly readable.
throw new Exception($error->response->body->description);
}
}
public function getPaymentMethodDescription() {
return pht('Add Credit or Debit Card');
}
public function getPaymentMethodIcon() {
return 'Balanced';
}
public function getPaymentMethodProviderDescription() {
return pht('Processed by Balanced');
}
public function getDefaultPaymentMethodDisplayName(
PhortunePaymentMethod $method) {
return pht('Credit/Debit Card');
}
protected function executeCharge(
PhortunePaymentMethod $method,
PhortuneCharge $charge) {
$this->loadBalancedAPILibraries();
$price = $charge->getAmountAsCurrency();
// Build the string which will appear on the credit card statement.
$charge_as = new PhutilURI(PhabricatorEnv::getProductionURI('/'));
$charge_as = $charge_as->getDomain();
$charge_as = id(new PhutilUTF8StringTruncator())
->setMaximumBytes(22)
->setTerminator('')
->truncateString($charge_as);
try {
Balanced\Settings::$api_key = $this->getSecretKey();
$card = Balanced\Card::get($method->getMetadataValue('balanced.cardURI'));
$debit = $card->debit($price->getValueInUSDCents(), $charge_as);
} catch (RESTful\Exceptions\HTTPError $error) {
// NOTE: This exception doesn't print anything meaningful if it escapes
// to top level. Replace it with something slightly readable.
throw new Exception($error->response->body->description);
}
$expect_status = 'succeeded';
if ($debit->status !== $expect_status) {
throw new Exception(
pht(
'Debit failed, expected "%s", got "%s".',
$expect_status,
$debit->status));
}
$charge->setMetadataValue('balanced.debitURI', $debit->uri);
$charge->save();
}
protected function executeRefund(
PhortuneCharge $charge,
PhortuneCharge $refund) {
$this->loadBalancedAPILibraries();
$debit_uri = $charge->getMetadataValue('balanced.debitURI');
if (!$debit_uri) {
throw new Exception(pht('No Balanced debit URI!'));
}
$refund_cents = $refund
->getAmountAsCurrency()
->negate()
->getValueInUSDCents();
$params = array(
'amount' => $refund_cents,
);
try {
Balanced\Settings::$api_key = $this->getSecretKey();
$balanced_debit = Balanced\Debit::get($debit_uri);
$balanced_refund = $balanced_debit->refunds->create($params);
} catch (RESTful\Exceptions\HTTPError $error) {
throw new Exception($error->response->body->description);
}
$refund->setMetadataValue('balanced.refundURI', $balanced_refund->uri);
$refund->save();
}
public function updateCharge(PhortuneCharge $charge) {
$this->loadBalancedAPILibraries();
$debit_uri = $charge->getMetadataValue('balanced.debitURI');
if (!$debit_uri) {
throw new Exception(pht('No Balanced debit URI!'));
}
try {
Balanced\Settings::$api_key = $this->getSecretKey();
$balanced_debit = Balanced\Debit::get($debit_uri);
} catch (RESTful\Exceptions\HTTPError $error) {
throw new Exception($error->response->body->description);
}
// TODO: Deal with disputes / chargebacks / surprising refunds.
}
private function getMarketplaceID() {
return $this
->getProviderConfig()
->getMetadataValue(self::BALANCED_MARKETPLACE_ID);
}
private function getSecretKey() {
return $this
->getProviderConfig()
->getMetadataValue(self::BALANCED_SECRET_KEY);
}
private function getMarketplaceURI() {
return '/v1/marketplaces/'.$this->getMarketplaceID();
}
/* -( Adding Payment Methods )--------------------------------------------- */
public function canCreatePaymentMethods() {
return true;
}
public function validateCreatePaymentMethodToken(array $token) {
return isset($token['balancedMarketplaceURI']);
}
/**
* @phutil-external-symbol class Balanced\Card
* @phutil-external-symbol class Balanced\Debit
* @phutil-external-symbol class Balanced\Settings
* @phutil-external-symbol class Balanced\Marketplace
* @phutil-external-symbol class Balanced\APIKey
* @phutil-external-symbol class RESTful\Exceptions\HTTPError
*/
public function createPaymentMethodFromRequest(
AphrontRequest $request,
PhortunePaymentMethod $method,
array $token) {
$this->loadBalancedAPILibraries();
$errors = array();
$account_phid = $method->getAccountPHID();
$author_phid = $method->getAuthorPHID();
$description = $account_phid.':'.$author_phid;
try {
Balanced\Settings::$api_key = $this->getSecretKey();
$card = Balanced\Card::get($token['balancedMarketplaceURI']);
$buyer = Balanced\Marketplace::mine()->createBuyer(
null,
$card->uri,
array(
'description' => $description,
));
} catch (RESTful\Exceptions\HTTPError $error) {
// NOTE: This exception doesn't print anything meaningful if it escapes
// to top level. Replace it with something slightly readable.
throw new Exception($error->response->body->description);
}
$method
->setBrand($card->brand)
->setLastFourDigits($card->last_four)
->setExpires($card->expiration_year, $card->expiration_month)
->setMetadata(
array(
'type' => 'balanced.account',
'balanced.accountURI' => $buyer->uri,
'balanced.cardURI' => $card->uri,
));
return $errors;
}
public function renderCreatePaymentMethodForm(
AphrontRequest $request,
array $errors) {
$ccform = id(new PhortuneCreditCardForm())
->setUser($request->getUser())
->setErrors($errors)
->addScript('https://js.balancedpayments.com/v1/balanced.js');
Javelin::initBehavior(
'balanced-payment-form',
array(
'balancedMarketplaceURI' => $this->getMarketplaceURI(),
'formID' => $ccform->getFormID(),
));
return $ccform->buildForm();
}
private function getBalancedShortErrorCode($error_code) {
$prefix = 'cc:balanced:';
if (strncmp($error_code, $prefix, strlen($prefix))) {
return null;
}
return substr($error_code, strlen($prefix));
}
public function translateCreatePaymentMethodErrorCode($error_code) {
$short_code = $this->getBalancedShortErrorCode($error_code);
if ($short_code) {
static $map = array(
);
if (isset($map[$short_code])) {
return $map[$short_code];
}
}
return $error_code;
}
public function getCreatePaymentMethodErrorMessage($error_code) {
$short_code = $this->getBalancedShortErrorCode($error_code);
if (!$short_code) {
return null;
}
switch ($short_code) {
default:
break;
}
return null;
}
private function loadBalancedAPILibraries() {
$root = dirname(phutil_get_library_root('phabricator'));
require_once $root.'/externals/httpful/bootstrap.php';
require_once $root.'/externals/restful/bootstrap.php';
require_once $root.'/externals/balanced-php/bootstrap.php';
}
}

View file

@ -1,57 +0,0 @@
/**
* @provides javelin-behavior-balanced-payment-form
* @requires javelin-behavior
* javelin-dom
* phortune-credit-card-form
*/
JX.behavior('balanced-payment-form', function(config) {
balanced.init(config.balancedMarketplaceURI);
function onsubmit(card_data) {
var errors = [];
if (!balanced.card.isCardNumberValid(card_data.number)) {
errors.push('cc:invalid:number');
}
if (!balanced.card.isSecurityCodeValid(card_data.number, card_data.cvc)) {
errors.push('cc:invalid:cvc');
}
if (!balanced.card.isExpiryValid(card_data.month, card_data.year)) {
errors.push('cc:invalid:expiry');
}
if (errors.length) {
ccform.submitForm(errors);
return;
}
var data = {
card_number: card_data.number,
security_code: card_data.cvc,
expiration_month: card_data.month,
expiration_year: card_data.year
};
balanced.card.create(data, onresponse);
}
function onresponse(response) {
var token = null;
var errors = [];
if (response.error) {
errors = ['cc:balanced:error:' + response.error.type];
} else if (response.status != 201) {
errors = ['cc:balanced:http:' + response.status];
} else {
token = response.data.uri;
}
ccform.submitForm(errors, {balancedMarketplaceURI: token});
}
var ccform = new JX.PhortuneCreditCardForm(JX.$(config.formID), onsubmit);
});