You are viewing revision #3 of this wiki article.
This is the latest version of this article.
You may want to see the changes made in this revision.
Preparation ¶
- Sign up a developer account at https://devep2.datamax.bg/ep2/epay2_demo
- Set up merchant and client username and password in ePay and notify URL address in the dev account (the same operations would be done for a live account too).
- Edit protected/config/main.php and add blocks of code for LIVE and DEV environment.
// Define LIVE constant as true if 'localhost' is not present in the host name. Configure the detecting of environment as necessary of course.
defined('LIVE') || define('LIVE', strpos($_SERVER['HTTP_HOST'],'localhost')===false ? true : false);
if (LIVE) {
define('EPAY_URL','https://www.epay.bg/');
define('EPAY_CLIENT_EMAIL','*********');
define('EPAY_CLIENT_NUMBER','********');
define('EPAY_SECRET_KEY','********');
}else{
define('EPAY_URL','https://devep2.datamax.bg/ep2/epay2_demo/');
define('EPAY_CLIENT_EMAIL','*********');
define('EPAY_CLIENT_NUMBER','********');
define('EPAY_SECRET_KEY','********');
}
Implementation ¶
I. In the view script add the following hidden fields:
<?php
$form=$this->beginWidget('CActiveForm', array(
'id'=>'orderForm',
'htmlOptions'=>array('onsubmit'=>'return false;'),
'action'=>EPAY_URL,
));
// epay.bg fields
echo CHtml::hiddenField('PAGE','paylogin');
echo CHtml::hiddenField('ENCODED','',array('id'=>'epayEncoded'));
echo CHtml::hiddenField('CHECKSUM','',array('id'=>'epayChecksum'));
echo CHtml::hiddenField('URL_OK',Yii::app()->createAbsoluteUrl('order/success'));
echo CHtml::hiddenField('URL_CANCEL',Yii::app()->createAbsoluteUrl('order/canceled'));
II. Further down in the same view file we will put JavaScript code that will send request to OrderController::actionCreate() to create the order record in the database and on success we will send another request to OrderController::actionEpayData() to generate the needed data for the hidden fields ENCODING and CHECKSUM.
$.post('<?php echo Yii::app()->createUrl('order/create'); ?>',$('#orderForm').serializeArray(),function(orderResp) {
if(orderResp.error === undefined){
$.post('<?php echo url('order/epayData')?>',{orderId:orderResp.id, price:orderResp.total, productName:'<?php echo $productLang->title?>'}, function(epayResp){
$('#epayEncoded').val(epayResp.encoded);
$('#epayChecksum').val(epayResp.checksum);
$('#orderForm').attr({action:'<?php echo EPAY_URL?>',onsubmit:true}).submit();
},'json');
}
}else{
alert(orderResp.error);
},'json');
III. In the class OrderController add these methods:
public function actionEpayData(){
$epay=new EpayBg();
echo CJavaScript::jsonEncode($epay->getInitData($_POST));
Yii::app()->end();
}
public function actionEpayNotify(){
$epay = new EpayBg();
$epay->notify();
}
IV. Create a file in protected/components/EpayBg.php
class EpayBg {
public function getInitData($post){
$dt=new DateTime('+1 day');
$expDate=$dt->format('d.m.Y');
$min=EPAY_CLIENT_NUMBER;
$data = <<<DATA
MIN={$min}
INVOICE={$post['orderId']}
AMOUNT={$post['price']}
EXP_TIME={$expDate}
DESCR={$post['productName']}
DATA;
# XXX Packet:
# (MIN or EMAIL)= REQUIRED
# INVOICE= REQUIRED
# AMOUNT= REQUIRED
# EXP_TIME= REQUIRED
# DESCR= OPTIONAL
$encoded = base64_encode($data);
return array(
'encoded'=>$encoded,
'checksum'=>$this->hmac('sha1', $encoded, EPAY_SECRET_KEY)
);
}
public function notify(){
$logCat='epay';
if(empty($_POST['encoded']) || empty($_POST['checksum'])){
Yii::log('Missing encoded or checksum POST variables', CLogger::LEVEL_INFO, $logCat);
}else{
$encoded = $_POST['encoded'];
$checksum = $_POST['checksum'];
$hmac = $this->hmac('sha1', $encoded, EPAY_SECRET_KEY); # XXX SHA-1 algorithm REQUIRED
if ($hmac == $checksum) { # XXX Check if the received CHECKSUM is OK
$data = base64_decode($encoded);
$lines_arr = split("\n", $data);
$infoData = '';
foreach ($lines_arr as $line) {
if (preg_match("/^INVOICE=(\d+):STATUS=(PAID|DENIED|EXPIRED)(:PAY_TIME=(\d+):STAN=(\d+):BCODE=([0-9a-zA-Z]+))?$/",
$line, $regs)) {
Yii::log($line,CLogger::LEVEL_INFO,$logCat);
$invoice = $regs[1]; // order id
$status = $regs[2];
$payDate = $regs[4]; # YYYYMMDDHHIISS
$stan = $regs[5]; # XXX if PAID
$bcode = $regs[6]; # XXX if PAID
# XXX process $invoice, $status, $payDate, $stan, $bcode here
# XXX if OK for this invoice
$infoData .= "INVOICE=$invoice:STATUS=OK\n";
if($status==='PAID'){
$model=Order::model()->findByPk($invoice);
if($model===null){
Yii::log($invoice.' order not found',CLogger::LEVEL_INFO,$logCat);
}else{
$model->setAttributes(array(
'payDate'=>implode('-',array(substr($payDate,0,4),substr($payDate,4,2),substr($payDate,6,2))).' '.
implode(':',array(substr($payDate,8,2),substr($payDate,10,2),substr($payDate,12,2))),
'stan'=>$stan,
'bcode'=>$bcode,
'statusId'=>Order::STATUS_PAID
));
$model->save();
Product::deductQty($model);
Product::sendSuccessEmails($model);
}
}
}
}
echo $infoData, "\n";
}
else {
echo "ERR=Not valid CHECKSUM\n";
Yii::log('ERR=Not valid CHECKSUM',CLogger::LEVEL_ERROR,$logCat);
}
}
}
private function hmac($algo,$data,$passwd){
/* md5 and sha1 only */
$algo=strtolower($algo);
$p=array('md5'=>'H32','sha1'=>'H40');
if(strlen($passwd)>64)
$passwd=pack($p[$algo],$algo($passwd));
if(strlen($passwd)<64)
$passwd=str_pad($passwd,64,chr(0));
$ipad=substr($passwd,0,64) ^ str_repeat(chr(0x36),64);
$opad=substr($passwd,0,64) ^ str_repeat(chr(0x5C),64);
return($algo($opad.pack($p[$algo],$algo($ipad.$data))));
}
}
V. The Order model would start with:
class Order extends CActiveRecord
{
const STATUS_INITIATED = 1;
const STATUS_CANCELED = 2;
const STATUS_EXPIRED = 3;
const STATUS_PAID = 4;
public $statuses = array(
self::STATUS_INITIATED => 'Initiated',
self::STATUS_CANCELED => 'Canceled',
self::STATUS_EXPIRED => 'Expired',
self::STATUS_PAID => 'Paid',
);
// more code of the model
Final words ¶
The classes OrderController and EpayBg have methods for handling both initializing the payment and the notification they send about the payment status. Epay will be sending notifications until the notify script sends correct message i.e. "INVOICE=$invoice:STATUS=OK\n". Make sure there's no other output or they will continue sending notifications. Good luck.
Браво
Много полезно четиво. Браво!!!
Полезно
Браво, наистина много полезно.
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.