Managing Credits In Different Currencies

Hi, my site allows users (buyers) to buy e-learning courses for other users (receivers).

The courses are different prices and are in a mix of different currencies such as USD, EUR, AUD and users pay for them with paypal.

If the receiver does not start the course within 28 days the the buyer gets a refund back but paypal does not like issuing all of these refunds, so i am thinking of changing my system to a credit system instead of a refund.

For example, if the buyer buys a $10 course and it is not started, the buyers site account is credited $10.

My worry is, how can i manage the currency conversion etc. As the site takes the initial payment of $10, then credits the buyers site account with $10, but what if the buyer then wants to buy a €7 course for another user and that gets refunded and so on and so on… how can i manage the currencies and credits as exchange rates need to be managed but a user is only paying with credits the second time around… thanks in advance for your help.

Is there a reason why you do not charge the buyer until the course is actually started?

The buyer is charged immediately when the buy the course…

Since exchange rates change frequently, you’ll need to pull the current rates from an external API. There are a bunch out there. Some are free some are not. Some require registration. Some have a very limited amount of currencies. Some API’s are a pain to implement.

Here’s a very simple script I just wrote using an API that’s free, requires no registration, has a simple JSON output, and updates the exchange rates every two minutes.

It’s a real quick and dirty implementation, but you’ll get the idea. You may want to implement it in a more portable class or something.


<?php

// Function to retrieve the value (if set) of a GET variable, or return a default
function getVar($var, $default = null)
{
    if ((isset($_GET[$var])) && ($_GET[$var] != '')) {
        return trim($_GET[$var]);
    }
    
    return $default;
}

$from_amt     = getVar('amt', 1);
$from         = strtoupper(getVar('from', 'USD'));
$to         = strtoupper(getVar('to', 'EUR'));
$api_url = 'http://www.getexchangerates.com/api/latest.json';

// Make sure $from_amt is a number
if (!is_numeric($from_amt)) {
    exit("The amount you are converting from must be a valid numeric value.");
}

// Grab the JSON from the converter API
$api_feed = file_get_contents($api_url);
if ($api_feed === false) {
    exit("There was an error pulling the data from {$api_url}");
}

// Convert the JSON to an easy-to-use array
$exchange_rates = json_decode($api_feed, true);
if ($exchange_rates == null) {
    exit("Unable to decode JSON feed from API");
}
$exchange_rates = $exchange_rates[0];

// Make sure $from is a supported/valid currency
if (!array_key_exists($from, $exchange_rates)) {
    exit("\\"{$from}\\" is either NOT a supported exchange rate, or it is an invalid one.");
}

// Make sure $to is a supported/valid currency
if (!array_key_exists($to, $exchange_rates)) {
    exit("\\"{$to}\\" is either NOT a supported exchange rate, or it is an invalid one.");
}

// The API used in this example uses USD as it's base line.
// If $from is not USD, we'll need to first convert it to USD
// before we can convert it to $to.
$usd_amt = ($from != 'USD') ? $from_amt / $exchange_rates[$from] : $from_amt;

// Convert it
$new_amt = $usd_amt * $exchange_rates[$to];

echo "{$from} {$from_amt} = {$to} {$new_amt}<br><br>";
echo "Exchange Rate data was last updated on: " . date('Y-m-d H:i:s T', $exchange_rates['DateTime']);

?>

I threw together a class for that API … just because. Suggestions and feedback are welcome.


/*************************************************************************
 A PHP class to convert currencies using the free exchange rate API
 at http://www.getexchangerates.com/
 
 Created by Keith Duvall (http://www.duvalltech.com/
 Copyright (C) Oct 2012
 
 You may freely use, modify, and share this however you like -- personal or 
 commercial -- as long as you don't charge to distribute it. 
 
 Please give credit where credit is due.
*************************************************************************/
class CurrencyExchange
{
    // API info
    protected $api_url = 'http://www.getexchangerates.com/api/latest.json';
    protected $feed_expires = true;
    protected $expires_in = 120; // Seconds
    protected $base_currency = 'USD'; // Currency the feed is based on
    
    // Defaults
    protected $from_currency = 'USD';
    protected $to_currency = 'EUR';
    
    // Don't edit these
    protected $last_update = null;
    protected $exchange_rates = array();
    
    public function __construct()
    {
        $this->refreshFeed();
    }
    
    protected function throwEx($msg = "Unknown")
    {
        throw new Exception($msg);
    }
    
    /**
    * Returns the time since the API feed was last updated.
    * 
    * Valid arguments are:
    * minutes: Minutes since it was last updated.
    * seconds: seconds since it was last updated.
    * absolute: absolute update time in the form of a unix EPOCH timestamp
    */
    public function lastUpdate($format = 'absolute')
    {
        switch ($format)
        {
            case "minutes":
                return (time() - $this->last_update) / 60;
                break;
            case "seconds":
                return time() - $this->last_update;
                break;
            default:
                return $this->last_update;
                break;
        }
    }
    
    /**
    * Tells you whether the feed has expired based on $this->expires_in
    */
    public function feedExpired()
    {
        if ((time() - $this->last_update) >= $this->expires_in) { return true; }
        return false;
    }
        
    /**
    * Returns an array of all the conversion rates
    */
    public function listRates()
    {
        return $this->exchange_rates;
    }
    
    /**
    * Converts $amount from $from currency to $to currency and returns the new amount as a float.
    * If $from or $to are null, it will use the class properties above.
    */
    public function convert($amount, $from = null, $to = null)
    {
        $from = ($from == null) ? strtoupper($this->from_currency) : strtoupper($from);
        $to = ($to == null) ? strtoupper($this->to_currency) : strtoupper($to);
        
        if (!is_numeric($amount)) {
            $this->throwEx("The amount to convert must be numeric.");
        }
        
        // Make sure the exchange rates have been pulled and the feed is current
        if ($this->last_update == null) { $this->refreshFeed(); }
        if ($this->feed_expires && $this->feedExpired()) { $this->refreshFeed(); }
        
        // Make sure $from and $to is valid and supported by the API
        if (!array_key_exists($from, $this->exchange_rates)) {
            $this->throwEx("Invalid or unsupported currency: {$from}");
        }
        if (!array_key_exists($to, $this->exchange_rates)) {
            $this->throwEx("Invalid or unsupported currency: {$to}");
        }
        
        // Convert the amount given to the base currency
        $base_amount = ($from != strtoupper($this->base_currency)) ? $amount / $this->exchange_rates[$from] : $amount;
        
        // Convert to new currency
        $new_amount = $base_amount * $this->exchange_rates[$to];
        
        return $new_amount;
    }
    
    /**
    * Pulls the JSON data from the API and converts the rates
    * into a more usable array.
    */
    public function refreshFeed()
    {
        if (!$raw_data = file_get_contents($this->api_url)) { 
            $this->throwEx("There was an error pulling the feed from the API."); 
        }
        
        if (!$data = json_decode($raw_data, true)) { 
            $this->throwEx("The feed API returned invalid JSON data."); 
        }
        
        $data = $data[0]; // Strip outer layer
        
        $this->last_update = $data['DateTime'];
        unset($data['DateTime']);
        
        $this->exchange_rates = $data;
        unset($data, $raw_data);
    }

}

Excellent, thanks a million for this…