Open Source Billing
This commit is contained in:
34
modules/checkout/classes/Checkout/Plugin.php
Normal file
34
modules/checkout/classes/Checkout/Plugin.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
/**
|
||||
* This class provides CHECKOUT Plugin Support
|
||||
*
|
||||
* @package Checkout
|
||||
* @category Plugins
|
||||
* @author Deon George
|
||||
* @copyright (c) 2009-2013 Open Source Billing
|
||||
* @license http://dev.osbill.net/license.html
|
||||
* @todo Does this need to be Serializable?
|
||||
*/
|
||||
abstract class Checkout_Plugin implements Serializable {
|
||||
protected $co; // Our Checkout Object
|
||||
protected $_object;
|
||||
|
||||
// Our required abstract classes
|
||||
public function serialize() {
|
||||
return (string)$this->_object;
|
||||
}
|
||||
public function unserialize($s) {
|
||||
$this->_object = XML::factory(NULL,NULL,$s);
|
||||
}
|
||||
|
||||
// Required abstract classes
|
||||
// Present pre-plugin processing information
|
||||
abstract public function before(Cart $co);
|
||||
abstract public function notify(Model_Checkout_Notify $cno);
|
||||
|
||||
public function __construct(Model_Checkout $co) {
|
||||
$this->co = $co;
|
||||
}
|
||||
}
|
||||
?>
|
204
modules/checkout/classes/Checkout/Plugin/Paypal.php
Normal file
204
modules/checkout/classes/Checkout/Plugin/Paypal.php
Normal file
@@ -0,0 +1,204 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
/**
|
||||
* This class provides PAYPAL support
|
||||
*
|
||||
* @package Checkout
|
||||
* @category Plugins
|
||||
* @author Deon George
|
||||
* @copyright (c) 2009-2013 Open Source Billing
|
||||
* @license http://dev.osbill.net/license.html
|
||||
*/
|
||||
abstract class Checkout_Plugin_Paypal extends Checkout_Plugin {
|
||||
protected $url_prod = 'www.paypal.com';
|
||||
protected $url_test = 'www.sandbox.paypal.com';
|
||||
private $ipn_test = '173.0.82.126';
|
||||
|
||||
protected $curlopts = array(
|
||||
CURLOPT_CONNECTTIMEOUT => 60,
|
||||
CURLOPT_FAILONERROR => TRUE,
|
||||
CURLOPT_FOLLOWLOCATION => FALSE,
|
||||
CURLOPT_HEADER => FALSE,
|
||||
CURLOPT_HTTPPROXYTUNNEL => FALSE,
|
||||
CURLOPT_RETURNTRANSFER => TRUE,
|
||||
CURLOPT_TIMEOUT => 30,
|
||||
CURLOPT_SSL_VERIFYHOST => FALSE,
|
||||
CURLOPT_SSL_VERIFYPEER => FALSE,
|
||||
CURLOPT_VERBOSE => FALSE,
|
||||
);
|
||||
|
||||
/**
|
||||
* User return from Paypal after payment
|
||||
*/
|
||||
public function after(Cart $co) {
|
||||
SystemMessage::add(array(
|
||||
'title'=>_('Payment Processing'),
|
||||
'type'=>'info',
|
||||
'body'=>sprintf('Thank you for your payment with paypal. It will be processed and applied to your cart items automatically in due course.'),
|
||||
));
|
||||
|
||||
HTTP::redirect('/');
|
||||
}
|
||||
|
||||
/**
|
||||
* User cancelled from Paypal and returned
|
||||
*/
|
||||
public function cancel(Cart $co) {
|
||||
SystemMessage::add(array(
|
||||
'title'=>_('Payment Cancelled'),
|
||||
'type'=>'info',
|
||||
'body'=>sprintf('Payment with Paypal was cancelled at your request.'),
|
||||
));
|
||||
|
||||
HTTP::redirect('cart');
|
||||
}
|
||||
|
||||
/**
|
||||
* Paypal payment notification and verification
|
||||
*/
|
||||
public function notify(Model_Checkout_Notify $cno) {
|
||||
$debug_mode = Kohana::$config->load('debug')->checkout_notify;
|
||||
|
||||
// If testing
|
||||
if (! $cno->status OR $cno->processed OR ($debug_mode AND Request::$client_ip == $this->ipn_test))
|
||||
return ('Thank you');
|
||||
|
||||
$co = Cart::instance(isset($cno->data['custom']) ? $cno->data['custom'] : '');
|
||||
|
||||
if (! $co->contents())
|
||||
return _('Thank you!');
|
||||
|
||||
if (! $debug_mode) {
|
||||
$request = Request::factory(sprintf('https://%s/cgi-bin/webscr',isset($cno->data['test_ipn']) ? $this->url_test : $this->url_prod))
|
||||
->method('POST');
|
||||
|
||||
$request->client()->options(Arr::merge($this->curlopts,array(
|
||||
CURLOPT_POSTFIELDS => Arr::merge(array('cmd'=>'_notify-validate'),$cno->data),
|
||||
)));
|
||||
|
||||
$response = $request->execute();
|
||||
}
|
||||
|
||||
switch ($debug_mode ? 'VERIFIED' : $response->body()) {
|
||||
case 'VERIFIED':
|
||||
// Verify that the IPN is for us.
|
||||
// @todo This should be in the DB.
|
||||
if ($cno->data['business'] == 'deon_1260578114_biz@graytech.net.au') {
|
||||
switch ($cno->data['payment_status']) {
|
||||
case 'Completed':
|
||||
// Our cart items total.
|
||||
$total = $co->total();
|
||||
$po = ORM::factory('Payment');
|
||||
|
||||
// Does the payment cover the cart total?
|
||||
if ($this->co->fee_passon AND $cno->data['mc_gross'] == $total+$this->co->fee($total)) {
|
||||
// Store the amounts in an array, so we can pro-rata the fee to each item.
|
||||
$amts = array();
|
||||
|
||||
// Add the fee to each item (pro-rated)
|
||||
for ($c=1;$c<=$cno->data['num_cart_items'];$c++) {
|
||||
// The payment fee - there should only be 1 of these, and it should be the last item.
|
||||
// We assume fees are added to $po->items() which are invoices.
|
||||
if (preg_match('/^0:/',$cno->data['item_number'.$c])) {
|
||||
$i = $j = 0;
|
||||
foreach ($po->items() as $pio) {
|
||||
$io = ORM::factory('Invoice',$pio->invoice_id);
|
||||
// @todo Need to do tax.
|
||||
$iio = $io->add_item();
|
||||
$iio->quantity = 1;
|
||||
$iio->module_id = $cno->mid()->id;
|
||||
$iio->item_type = 125; // Payment Fee
|
||||
$iio->price_base = (++$j==count($amts)) ? $cno->data['mc_gross_'.$c]-$i : Currency::round($pio->alloc_amt/array_sum($amts)*$cno->data['mc_gross_'.$c]);
|
||||
$iio->date_start = $iio->date_stop = time();
|
||||
|
||||
// @todo Validate Save
|
||||
$io->save();
|
||||
|
||||
$pio->alloc_amt = ($pio->alloc_amt+$iio->price_base > $pio->invoice->total()) ? $pio->alloc_amt : $pio->alloc_amt+$iio->price_base;
|
||||
$i += $iio->price_base;
|
||||
}
|
||||
|
||||
} elseif (is_numeric($cno->data['item_number'.$c])) {
|
||||
array_push($amts,$cno->data['mc_gross_'.$c]);
|
||||
$cio = ORM::factory('cart',$cno->data['item_number'.$c]);
|
||||
|
||||
if ($cio->loaded())
|
||||
switch ($cio->motype()) {
|
||||
case 'invoice':
|
||||
// Validate we are all the same account
|
||||
// @todo Need to handle if the cart has more than 1 account.
|
||||
if (! $po->account_id AND $cio->mo()->account_id) {
|
||||
$po->account_id = $cio->mo()->account_id;
|
||||
|
||||
} elseif ($po->account_id != $cio->mo()->account_id) {
|
||||
throw new Kohana_Exception('Unable to handle payments for multiple accounts');
|
||||
|
||||
}
|
||||
|
||||
$po->add_item($cio->module_item)->alloc_amt = $cno->data['mc_gross_'.$c];
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Kohana_Exception('Dont know how to handle :item',array(':item',$cio->motype()));
|
||||
}
|
||||
|
||||
// Dont know how to handle this item.
|
||||
} else {
|
||||
// @todo
|
||||
}
|
||||
}
|
||||
|
||||
// @todo Validate Save
|
||||
$po->account_id = $cio->mo()->account_id;
|
||||
$po->fees_amt = $cno->data['mc_fee'];
|
||||
$po->total_amt = $cno->data['mc_gross'];
|
||||
$po->date_payment = strtotime($cno->data['payment_date']);
|
||||
$po->checkout_plugin_id = $this->co->id;
|
||||
$po->notes = $cno->data['txn_id'];
|
||||
$po->save();
|
||||
|
||||
// Clear the cart
|
||||
if (! $debug_mode)
|
||||
$co->delete();
|
||||
|
||||
} elseif (! $this->co->fee_passon AND $cno->data['mc_gross']-$cno->data['mc_fee'] == $total) {
|
||||
// Ignore the fee
|
||||
|
||||
} else {
|
||||
echo Debug::vars('IPN doesnt match cart total');
|
||||
// If there is more than 1 item in the cart, we'll leave it to an admin to process.
|
||||
if ($cno->data['num_cart_items'] == 1) {
|
||||
echo Debug::vars('Apply to cart item');
|
||||
} else {
|
||||
// @todo - add the payment, with no payment items
|
||||
echo Debug::vars('Leave for admin');
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'Refunded':
|
||||
default:
|
||||
throw new Kohana_Exception('Unable to handle payments of type :type',array(':type'=>$cno->data['payment_status']));
|
||||
}
|
||||
|
||||
} else {
|
||||
$cno->status = FALSE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'INVALID':
|
||||
default:
|
||||
$cno->status = FALSE;
|
||||
}
|
||||
|
||||
$cno->processed = TRUE;
|
||||
if (! $debug_mode)
|
||||
$cno->save();
|
||||
|
||||
return _('Processed, thank you!');
|
||||
}
|
||||
}
|
||||
?>
|
55
modules/checkout/classes/Checkout/Plugin/Paypal/Cart.php
Normal file
55
modules/checkout/classes/Checkout/Plugin/Paypal/Cart.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
/**
|
||||
* This class provides PAYPAL CART support
|
||||
*
|
||||
* @package Checkout
|
||||
* @category Plugins
|
||||
* @author Deon George
|
||||
* @copyright (c) 2009-2013 Open Source Billing
|
||||
* @license http://dev.osbill.net/license.html
|
||||
*/
|
||||
class Checkout_Plugin_Paypal_Cart extends Checkout_Plugin_Paypal {
|
||||
private $test_mode = FALSE;
|
||||
|
||||
/**
|
||||
* Set payment via Paypal
|
||||
*/
|
||||
public function before(Cart $co) {
|
||||
$output = '';
|
||||
|
||||
$output .= View::factory('checkout/plugin/paypal/before')
|
||||
->set('checkout',$this->co)
|
||||
->set('cart',$co);
|
||||
|
||||
$output .= Form::open(sprintf('https://%s/cgi-bin/webscr',$this->test_mode ? $this->url_test : $this->url_prod),array('method'=>'POST'));
|
||||
$output .= Form::hidden('cmd','_cart');
|
||||
$output .= Form::hidden('business',$this->test_mode ? 'deon_1260578114_biz@graytech.net.au' : 'deon@graytech.net.au');
|
||||
$output .= Form::hidden('bn','Graytech_BuyNow_WPS_AU');
|
||||
$output .= Form::hidden('cancel_return',URL::site('checkout/cancel/'.$this->co->id,TRUE));
|
||||
$output .= Form::hidden('custom',$co->id());
|
||||
// @todo This should be dynamic
|
||||
$output .= Form::hidden('currency_code','AUD');
|
||||
$output .= Form::hidden('notify_url',URL::site('checkout/notify/'.$this->co->id,TRUE));
|
||||
$output .= Form::hidden('return',URL::site('checkout/after/'.$this->co->id,TRUE));
|
||||
$output .= Form::hidden('upload','1');
|
||||
|
||||
$c = 1;
|
||||
foreach ($co->contents() as $cio) {
|
||||
$output .= Form::hidden('item_number_'.$c,$cio->id);
|
||||
$output .= Form::hidden('item_name_'.$c,$cio->item()->i);
|
||||
$output .= Form::hidden('amount_'.$c,$cio->item()->t);
|
||||
$c++;
|
||||
}
|
||||
|
||||
$output .= Form::hidden('item_number_'.$c,'0:PAYFEE');
|
||||
$output .= Form::hidden('item_name_'.$c,'Paypal Fee');
|
||||
$output .= Form::hidden('amount_'.$c,$this->co->fee($co->total()));
|
||||
|
||||
$output .= Form::submit('submit','Pay Now');
|
||||
$output .= Form::close();
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
?>
|
Reference in New Issue
Block a user