Skip to content

Instantly share code, notes, and snippets.

@deanpcmad
Last active December 24, 2015 12:19
Show Gist options
  • Save deanpcmad/6796934 to your computer and use it in GitHub Desktop.
Save deanpcmad/6796934 to your computer and use it in GitHub Desktop.
<?php
require 'vendor/autoload.php';
/**
* Configuration
*/
$CONFIG = [
'timezone' => 'Europe/London',
'stripe' => [
'secret_key' => '-'
],
'freeagent' => [
// OAuth keys, found in the FreeAgent developer dashboard once an app is created
'client_id' => '-',
'client_secret' => '-',
// Your OAuth refresh token. To generate this see here:
// https://dev.freeagent.com/docs/quick_start
// Remember to replace the sandbox API URL's with the proper ones.
// Once you follow instructions at that page, you should be able to find your
// refresh token in Google's OAuth Playground.
'refresh_token' => '-',
// Bank ID numbers as shown in query string when viewing bank account in FA.
// This saves an extra API call where we'd only be search and guessing the bank accounts anyway.
'bank_ids' => [
'stripe' => '-',
'current' => '-'
]
]
];
/**
* Time Periods
* Decide which time periods we're processing on this run
*/
date_default_timezone_set($CONFIG['timezone']);
$time_start = new DateTime('June 2013 00:00:00');
$time_end = new DateTime('yesterday 23:59:59');
echo '---------------------------------------------' .PHP_EOL;
echo 'Transactions from: ' . $time_start->format('c') . PHP_EOL;
echo 'Transactions until: ' . $time_end->format('c') .PHP_EOL;
echo '---------------------------------------------' .PHP_EOL;
/**
* Fetch Stripe Transactions
* Make the API request to fetch Stripe's transactions recursively
* Fetches $fetchEachRun events at a time until less than $fetchEachRun events is returned
*/
$stripeTransactions = [];
$fetchEachRun = 100;
$fetchOffset = 0;
Stripe::setApiKey($CONFIG['stripe']['secret_key']);
do {
$apiCall = Stripe_BalanceTransaction::all([
'count' => $fetchEachRun,
'offset' => $fetchOffset,
'created' => [
'gte' => $time_start->format('U'),
'lte' => $time_end->format('U')
]
]);
$fetchOffset += $fetchEachRun;
$stripeTransactions = array_merge($stripeTransactions, $apiCall->data);
echo 'Found ' . count($apiCall->data) . ' transactions at Stripe...' . PHP_EOL;
} while(count($apiCall->data) >= $fetchEachRun);
echo 'Found ' . number_format(count($stripeTransactions)) . ' total transactions at Stripe.' . PHP_EOL;
if(!count($stripeTransactions)) {
die('[ERROR] No events to move into FreeAgent! Exiting.' . PHP_EOL);
}
/**
* Generate CSV
* Walk through each Stripe event and add the event into our bank upload CSV
*/
echo 'Building the CSV file to be uploaded as a bank statement...' . PHP_EOL;
$csv = [];
foreach($stripeTransactions AS $transaction) {
// Decide which date we're going to use for this transaction
if($transaction->type == 'transfer') {
$time = $transaction->available_on;
} else {
$time = $transaction->created;
}
$date = DateTime::createFromFormat('U', $time)->format('d/m/Y');
// Turn the amount into pounds from pennies
$amount = $transaction->amount / 100;
// Create a meaningful description
if(empty($transaction->description)) {
$description = 'No Description';
} else {
$description = str_replace(['\'', '"', ','], '_', ucwords(strtolower($transaction->description)));
}
$description = sprintf('%s - %s [%s]', ucwords($transaction->type), $description, $transaction->id);
// Add this line to the CSV
$csv[] = [$date, $amount, $description];
// Record any fees for this transaction
if($transaction->fee != 0) {
// Turn the fee amount into whole points and reverse it
// E.g. a fee of £1.00 should show as £-1.00 from the FA Stripe account balance
$fee_amount = 0 - ($transaction->fee / 100);
// Describe this fee, based on the description above
if($fee_amount > 0) {
$fee_description = 'Fee Refund';
} else {
$fee_description = 'Fee';
}
$fee_description .= substr($description, strpos($description, '-') - 1);
// Add this fee as a line to the CSV
$csv[] = [$date, $fee_amount, $fee_description];
}
}
// Turn the CSV data into a valid format to be uploaded
$csv = implode(PHP_EOL, array_map(function($row) {
return implode(',', $row);
}, $csv));
// It's nice to keep an eye on things and see the CSV being generated
echo '---------------------------------------------' . PHP_EOL;
echo $csv . PHP_EOL;
echo '---------------------------------------------' . PHP_EOL;
/**
* Setup OAuth for FreeAgent
*/
$faOauth = new OAuth2\Client($CONFIG['freeagent']['client_id'], $CONFIG['freeagent']['client_secret']);
// Grab a fresh access token using our permanent refresh token
$faOauth->setAccessTokenType(OAuth2\Client::ACCESS_TOKEN_BEARER);
$response = $faOauth->getAccessToken('https://api.freeagent.com/v2/token_endpoint', 'refresh_token', ['refresh_token' => $CONFIG['freeagent']['refresh_token']]);
// Tell the oauth client library to use our fresh access token
$faOauth->setAccessToken($response['result']['access_token']);
/**
* Upload to FreeAgent
*/
$response = $faOauth->fetch(
sprintf('https://api.freeagent.com/v2/bank_transactions/statement?bank_account=%d', $CONFIG['freeagent']['bank_ids']['stripe']),
['statement' => $csv],
'POST',
['User-Agent' => 'N/A']
);
if($response['code'] != 200) {
echo '[ERROR] There was a problem with the FreeAgent API when making the statement upload.' . PHP_EOL;
echo '[API Error] ' . $response['result']['errors']['error']['message'] . PHP_EOL;
exit;
}
echo 'The CSV statement has been uploaded to FreeAgent.' . PHP_EOL;
/**
* Make explanations at FreeAgent
*/
$freeagentUnexplained = [];
$fetchEachRun = 100;
$fetchPage = 1;
do {
$response = $faOauth->fetch(
'https://api.freeagent.com/v2/bank_transactions',
[
'bank_account' => $CONFIG['freeagent']['bank_ids']['stripe'],
'view' => 'unexplained',
'per_page' => $fetchEachRun,
'page' => $fetchPage
],
'GET',
[
'User-Agent' => 'N/A',
'Content-Type' => 'application/json',
]
);
if($response['code'] != 200) {
echo '[ERROR] There was a problem with the FreeAgent API when fetching unexplained bank transactions.' . PHP_EOL;
// echo '[API Error] ' . $response['result']['errors']['error']['message'] . PHP_EOL;
exit;
}
$results = $response['result']['bank_transactions'];
$freeagentUnexplained = array_merge($freeagentUnexplained, $results);
$fetchPage++;
echo 'Found ' . count($results) . ' unexplained transactions in the Stripe account at FreeAgent...' . PHP_EOL;
} while(count($results) >= $fetchEachRun);
echo 'Found ' . number_format(count($freeagentUnexplained)) . ' total unexplained transactions in the Stripe account at FreeAgent.' . PHP_EOL;
foreach($freeagentUnexplained AS $transaction) {
$faTxnId = substr($transaction['url'], strrpos($transaction['url'], '/') + 1);
$faTxnType = strtolower(str_replace(' ', '_', substr($transaction['description'], 0, strpos($transaction['description'], '-') - 1)));
// Decide what we need to explain this particular transaction as
$data = [
];
if($faTxnType == 'transfer') {
$data['transfer_bank_account'] = $CONFIG['freeagent']['bank_ids']['current'];
}
if(stristr($transaction['description'], 'Fee'))
{
$data['category'] = "363";
$data['description'] = 'Stripe charge';
}
else
{
$data['category'] = "001";
// Removes the crap and leaves "Invoice #123" as the description
preg_match("/Invoice #[0-9]*/i", $transaction['description'], $output);
$data['description'] = $output[0];
}
// Try to explain this transaction at FreeAgent
printf('Explaining FreeAgent bank transaction %d [%s]...', $faTxnId, $faTxnType);
$response = $faOauth->fetch(
'https://api.freeagent.com/v2/bank_transaction_explanations',
json_encode([
'bank_transaction_explanation' => array_merge([
'bank_transaction' => $transaction['url'],
'dated_on' => $transaction['dated_on'],
'gross_value' => $transaction['amount'],
'description' => $transaction['description'],
], $data),
]),
'POST',
[
'User-Agent' => 'N/A',
'Content-Type' => 'application/json',
]
);
if($response['code'] == 200 || 201) {
echo 'OK.' . PHP_EOL;
} else {
echo 'FAILED.' . PHP_EOL;
var_dump($response['result']) . PHP_EOL;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment