Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Active record storage #4

Open
wants to merge 13 commits into
base: active-record-storage
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,6 @@
"payum/payum": "0.7.*@dev",
"yiisoft/yii": "~1"
},
"suggest": {
"payum/paypal-express-checkout-nvp": "If you want to use paypal express checkout nvp gateway",
"payum/paypal-pro-checkout-nvp": "If you want to use paypal pro checkout nvp gateway",
"payum/authorize-net-aim": "If you want to use authorize.net gateway",
"payum/be2bill": "If you want to use be2bill gateway",
"payum/payex": "If you want to use payex gateway",
"payum/omnipay-bridge": "If you want to use omnipay provided gateways"
},
"autoload": {
"psr-0": { "Payum\\YiiExtension": "" }
},
Expand Down
175 changes: 175 additions & 0 deletions docs/how-to-use-active-record-storage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# How to use active record storage

## Configuration

_**Note**: We assume you already [install the extension](installation.md) correctly._

In the _app/config/main.php_ you have to configure payum extensions.
In general you define model storages and payments.
Your configuration may look like this:

```php
<?php
// config/main.php

use Buzz\Client\Curl;
use Payum\Extension\StorageExtension;
use Payum\Paypal\ExpressCheckout\Nvp\Api;
use Payum\Paypal\ExpressCheckout\Nvp\PaymentFactory;

return array(
'controllerMap'=>array(
'payment'=>array(
'class'=>'Payum\YiiExtension\PaymentController',
),
),
'components' => array(
'payum' => array(
'class' => '\Payum\YiiExtension\PayumComponent',
'tokenStorage' => new Payum\YiiExtension\Storage\ActiveRecordStorage(
'TOKEN_TABLE_NAME', 'Payum\YiiExtension\Model\PaymentSecurityToken'
),
'payments' => array(
'paypal' => PaymentFactory::create(new Api(new Curl(), array(
'username' => 'REPLACE WITH YOURS',
'password' => 'REPLACE WITH YOURS',
'signature' => 'REPLACE WITH YOURS',
'sandbox' => true
)))
),
'storages' => array(
'paypal' => array(
'Payum\Paypal\ExpressCheckout\Nvp\Model\PaymentDetails' => new Payum\YiiExtension\Storage\ActiveRecordStorage(
'PAYMENT_DETAILS_TABLE_NAME',
'Payum\YiiExtension\Model\PaymentDetailsActiveRecordWrapper'
),
)
)
),
),
);
```
You will need to create database tables for token storage and payment details storage.

The token storage table can be created in a MySQL database with the following SQL:

```
--
-- Table structure for table `TOKEN_TABLE_NAME`
--

CREATE TABLE IF NOT EXISTS `TOKEN_TABLE_NAME` (
`_hash` varchar(255) NOT NULL DEFAULT '',
`_payment_name` varchar(255) DEFAULT NULL,
`_details` varchar(255) DEFAULT NULL,
`_after_url` varchar(255) DEFAULT NULL,
`_target_url` varchar(255) DEFAULT NULL,
PRIMARY KEY (`_hash`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```

The payment details table can be created as follows:
```
--
-- Table structure for table `PAYMENT_DETAILS_TABLE_NAME`
--

CREATE TABLE IF NOT EXISTS `PAYMENT_DETAILS_TABLE_NAME` (
`_id` int(11) NOT NULL AUTO_INCREMENT,
`_details` text,
PRIMARY KEY (`_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
```
The same table can be used to store payment details for multiple payment methods (e.g. Paypal, AuthorizeNet, etc.)
The payment details are serialized and stored in _details.

_**Note**: We use paypal as an example. You may configure any other payment you want._

## Usage
(The following code is unchanged from using FileSystemStorage - implementing ActiveRecordStorage
doesn't require changes to your code).
Here is the class you want start with.
The prepare action create payment details model.
Fill it with amount and currency and creates some tokens.
At the end it redirects you to payum capture controller which does all the job.
The done action is your landing action after the capture is finished.
Here you want to check the status and do your business actions depending on it.

```php
<?php
//app/controllers/PaypalController.php

class PaypalController extends CController
{
public function actionPrepare()
{
$paymentName = 'paypal';

$payum = $this->getPayum();

$tokenStorage = $payum->getTokenStorage();
$paymentDetailsStorage = $payum->getRegistry()->getStorageForClass(
'Payum\Paypal\ExpressCheckout\Nvp\Model\PaymentDetails',
$paymentName
);

$paymentDetails = $paymentDetailsStorage->createModel();
$paymentDetails['PAYMENTREQUEST_0_CURRENCYCODE'] = 'USD';
$paymentDetails['PAYMENTREQUEST_0_AMT'] = 1.23;
$paymentDetailsStorage->updateModel($paymentDetails);

$doneToken = $tokenStorage->createModel();
$doneToken->setPaymentName($paymentName);
$doneToken->setDetails($paymentDetailsStorage->getIdentificator($paymentDetails));
$doneToken->setTargetUrl(
$this->createAbsoluteUrl('paypal/done', array('payum_token' => $doneToken->getHash()))
);
$tokenStorage->updateModel($doneToken);

$captureToken = $tokenStorage->createModel();
$captureToken->setPaymentName('paypal');
$captureToken->setDetails($paymentDetailsStorage->getIdentificator($paymentDetails));
$captureToken->setTargetUrl(
$this->createAbsoluteUrl('payment/capture', array('payum_token' => $captureToken->getHash()))
);
$captureToken->setAfterUrl($doneToken->getTargetUrl());
$tokenStorage->updateModel($captureToken);

$paymentDetails['RETURNURL'] = $captureToken->getTargetUrl();
$paymentDetails['CANCELURL'] = $captureToken->getTargetUrl();
$paymentDetailsStorage->updateModel($paymentDetails);

$this->redirect($captureToken->getTargetUrl());
}

public function actionDone()
{
$token = $this->getPayum()->getHttpRequestVerifier()->verify($_REQUEST);
$payment = $this->getPayum()->getRegistry()->getPayment($token->getPaymentName());

$payment->execute($status = new \Payum\Request\BinaryMaskStatusRequest($token));

$content = '';
if ($status->isSuccess()) {
$content .= '<h3>Payment status is success.</h3>';
} else {
$content .= '<h3>Payment status IS NOT success.</h3>';
}

$content .= '<br /><br />'.json_encode(iterator_to_array($status->getModel()), JSON_PRETTY_PRINT);

echo '<pre>',$content,'</pre>';
exit;
}

/**
* @return \Payum\YiiExtension\PayumComponent
*/
private function getPayum()
{
return Yii::app()->payum;
}
}
```

Back to [index](index.md).
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

* [Installation](installation.md)
* [Get it started](get-it-started.md)
* [How to use active record storage](how-to-use-active-record-storage.md)
77 changes: 77 additions & 0 deletions src/Payum/YiiExtension/Model/PaymentActiveRecord.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php
/**
* This is the model class for table $tableName, which is being used for token storage for Payum payments
*
* The following are the available columns in table $tableName:
* @property string $_hash
* @property string $_payment_name
* @property string $_details
* @property string $_after_url
* @property string $_target_url
*
* Underscores are used because usually these would be private and to prevent
* the ActiveRecord getters and setters clashing with the required
* getters and setters from TokenInterface
*/

namespace Payum\YiiExtension\Model;

use Payum\Core\Exception\InvalidArgumentException;

class PaymentActiveRecord extends \CActiveRecord
{
private static $_tableName;

/**
* Constructs a model corresponding to table $tableName
* The table must have the columns identified above in the
* comments for this class.
*
* @param string $scenario
* @param $tableName
* @throws \Payum\Core\Exception\InvalidArgumentException
*/
public function __construct($scenario = 'insert', $tableName = '')
{
if ($scenario == 'insert' && $tableName == '') {
throw new InvalidArgumentException(
'Table name must be supplied when creating a new Payment'
);
}
if ($tableName !== '') {
self::$_tableName = $tableName;
}
parent::__construct($scenario);
/* if ($scenario == 'insert') {
$this->_hash = Random::generateToken();
} */
}

/**
* @return string the associated database table name
*/
public function tableName()
{
return self::$_tableName;
}

/**
* Returns the static model of the specified AR class.
* Please note that you should have this exact method in all your CActiveRecord descendants!
* @param string $tableName table corresponding to the model
* @param string $className active record class name.
* @return Payment the static model class
* @throws \Payum\Core\Exception\InvalidArgumentException
*/
public static function model($tableName, $className=__CLASS__)
{
if ($tableName == '') {
throw new InvalidArgumentException(
'Table name must be supplied when trying to find a Payment'
);
}
self::$_tableName = $tableName;
return parent::model($className);
}

}
2 changes: 1 addition & 1 deletion src/Payum/YiiExtension/Model/PaymentDetails.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

class PaymentDetails extends \CActiveRecord
{
/**
/**
* @var array
*/
public $array = array();
Expand Down
54 changes: 54 additions & 0 deletions src/Payum/YiiExtension/Model/PaymentDetailsActiveRecordWrapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php
/**
* User: martyn ling <[email protected]>
* Date: 05/12/13
* Time: 23:19
*/

namespace Payum\YiiExtension\Model;

use Payum\Core\Model\ArrayObject;

class PaymentDetailsActiveRecordWrapper extends ArrayObject
{
public $activeRecord;

public function __construct($scenario = 'insert', $tableName = '')
{
if ($scenario == 'insert') {
$this->activeRecord = new PaymentActiveRecord('insert', $tableName);
}
}

public function primaryKey()
{
return $this->activeRecord->tableSchema->primaryKey;
}

public function save()
{
$this->activeRecord->_details = serialize($this->array);
$this->activeRecord->save();
$this[$this->primaryKey()] = $this->activeRecord->{$this->primaryKey()};
}

public function delete()
{
$this->activeRecord->delete();
}

public static function findModelById($tableName, $id)
{
$paymentDetails = new PaymentDetailsActiveRecordWrapper('update');
$paymentDetails->activeRecord = PaymentActiveRecord::model($tableName)->findByPk($id);
$paymentDetails->refreshFromActiveRecord();

return $paymentDetails;
}

public function refreshFromActiveRecord()
{
$this[$this->primaryKey()] = $this->activeRecord->{$this->primaryKey()};
$this->array = unserialize($this->activeRecord->_details);
}
}
Loading