| Current Path : /var/www/html/crowdfunding/frontend/controllers/ |
| Current File : /var/www/html/crowdfunding/frontend/controllers/PaymentController.php |
<?php
namespace frontend\controllers;
/***
ini_set('display_startup_errors', 1);
ini_set('display_errors', 1);
error_reporting(-1);
***/
use Yii;
use yii\helpers\Url;
use yii\web\Controller;
use yii\filters\VerbFilter;
use backend\modules\payment\models\Payment;
use backend\modules\payment\models\PaymentCallback;
use backend\modules\payment\models\radiuz\Payment as RadiuzPayment;
use backend\modules\payment\models\radiuz\PaymentCallback as RadiuzCallback;
use common\components\payment\PaymentProxy;
use common\components\payment\provider\ZakatFpx;
use common\components\payment\provider\zakatfpx\PaymentFields;
use common\components\payment\provider\zakatfpx\models\PaymentProviderZakatfpx;
use common\components\payment\provider\zakatfpx\models\RadiuzPaymentProviderZakatfpx;
use frontend\models\PaymentRadiuz;
use bedezign\yii2\audit\components\web\ErrorHandler;
class PaymentController extends Controller
{
/**
* zakat type base on zakat fpx type code
*/
const ZAKAT_TYPE = '030';
/**
* crowd zakat order number prefix
*/
const CROWD_ZAKAT_PREFIX = 'CRW';
/**
* Affiliate zakat order number prefix
*/
const RADIUZ_ZAKAT_PREFIX = 'RDZ';
/**
* return URL for crowd
*/
public static function CHANNEL()
{
return Yii::$app->params['payment_gateway']['zakat_fpx']['channel'];
}
/**
* return URL for crowd
*/
public static function ID_PREFIX()
{
return Yii::$app->params['payment_gateway']['zakat_fpx']['id_prefix'];
}
/**
* return URL for crowd
*/
public static function BASE_CROWD_URL()
{
return Yii::$app->params['payment_gateway']['zakat_fpx']['response']['base_url']['crowd'];
}
/**
* return URL for crowd
*/
public static function BASE_RADIUZ_URL()
{
return Yii::$app->params['payment_gateway']['zakat_fpx']['response']['base_url']['radiuz'];
}
/**
* return URL for crowd
*/
public static function SUCCESS_URL()
{
return Yii::$app->params['payment_gateway']['zakat_fpx']['response']['success_url'];
}
/**
* return URL for radiuz
*/
public static function FAILED_URL()
{
return Yii::$app->params['payment_gateway']['zakat_fpx']['response']['failed_url'];
}
/**
* get response token
*/
public static function RESPONSE_TOKEN()
{
return Yii::$app->params['payment_gateway']['zakat_fpx']['response']['token'];
}
/**
* @inheritdoc
*/
public function beforeAction($action)
{
$whitelist = ['pay-callback', 'pay', 'response-zakat-fpx'];
if (in_array($action->id, $whitelist)) {
$this->enableCsrfValidation = false;
}
return parent::beforeAction($action);
}
/**
* @inheritdoc
*/
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'payment-redirect' => ['POST'],
'pay' => ['GET']
],
],
];
}
public function actionRequestZakatFpx($id)
{
try {
$payment = Payment::findOne($id);
/** check payment id, if it unused */
if (empty($payment) || $payment->payment_status != "pendingpay") {
# throw new \Exception('invalid payment id');
}
$uniqid = ZakatFpx::generateId(self::ID_PREFIX(), $id);
$payment_proxy = new PaymentProxy(
new ZakatFpx(
(new PaymentFields())
->name($payment->payment_payer_name)
->id_type($payment->payment_payer_id_type)
->id_number($payment->payment_payer_id_no)
->tel_number($payment->payment_payer_phone)
->zakat_type($payment->payment_zakat_type) # Zakat on Savings
->year($payment->payment_zakat_year)
->email($payment->payment_payer_email)
->uniqueid($uniqid)
->channel(self::CHANNEL())
->afcode($payment->payment_affiliate_code)
)
);
$form = '';
Yii::$app->db->transaction(function ($db) use (&$uniqid, &$payment_proxy, &$payment, &$form) {
/*** provider models * */
$provider_model = new PaymentProviderZakatfpx();
$provider_model->order_number = $uniqid;
if (!$provider_model->save()) {
throw new \Exception("Error saving payment data");
}
$form = $payment_proxy->pay(number_format((float) $payment->payment_total / 100, 2, '.', ''));
$payment->payment_gateway_refno = $uniqid;
$payment->payment_status = Payment::DEFAULT_PENDING_STATUS;
$payment->payment_provider_table_name = 'zakatfpx';
$payment->payment_provider_id = $provider_model->zakatfpx_id;
if (!$payment->save()) {
throw new \Exception("Error saving payment trace");
}
});
return $this->render('payment-request', compact('form'));
} catch (\Throwable $t) {
if (YII_ENV != 'prod')
throw $t;
(new ErrorHandler)->logException($t);
}
}
/*
public function actionRequestZakatFpxBypass($id)
{
try {
$payment = Payment::findOne($id);
if (empty($payment) || $payment->payment_status != "pendingpay") {
}
$uniqid = ZakatFpx::generateId(self::ID_PREFIX(), $id);
$payment_proxy = new PaymentProxy(
new ZakatFpx(
(new PaymentFields())
->name($payment->payment_payer_name)
->id_type($payment->payment_payer_id_type)
->id_number($payment->payment_payer_id_no)
->tel_number($payment->payment_payer_phone)
->zakat_type($payment->payment_zakat_type) # Zakat on Savings
->year($payment->payment_zakat_year)
->email($payment->payment_payer_email)
->uniqueid($uniqid)
->channel(self::CHANNEL())
->afcode('0')
)
);
$form = '';
Yii::$app->db->transaction(function ($db) use (&$uniqid, &$payment_proxy, &$payment, &$form) {
$provider_model = new PaymentProviderZakatfpx();
$provider_model->order_number = $uniqid;
if (!$provider_model->save()) {
throw new \Exception("Error saving payment data");
}
$form = $payment_proxy->pay(number_format($payment->payment_total / 100, 2));
$payment->payment_gateway_refno = $uniqid;
#$payment->payment_status = Payment::DEFAULT_PENDING_STATUS;
$payment->payment_status = 'verified';
$payment->payment_provider_table_name = 'zakatfpx';
$payment->payment_provider_id = $provider_model->zakatfpx_id;
if (!$payment->save()) {
throw new \Exception("Error saving payment trace");
}
});
#return $this->render('payment-request-bypass', compact('form'));
return $this->render('payment-request-bypass', ['payment_total' => number_format($payment->payment_total / 100, 2)]);
} catch (\Throwable $t) {
if (YII_ENV != 'prod')
throw $t;
(new ErrorHandler)->logException($t);
}
}
*/
/**
* Zakat response callback
*
*/
public function actionResponseZakatFpx()
{
try {
# Takes raw data from the request
$json = file_get_contents('php://input');
# Converts it into a PHP object
$data = json_decode($json);
if (empty($data)) {
throw new \Exception('Not a valid json object');
}
#validate response token
if (self::RESPONSE_TOKEN() !== $data->response_token) {
throw new \Exception('Unauthorize call to the endpoint');
}
# get prefix
$prefix = ZakatFpx::getPrefix($data->myTransID);
/** handle the data according order number type */
switch ($prefix) {
case $prefix === self::CROWD_ZAKAT_PREFIX:
/** save response */
$this->saveResponse(new PaymentCallback(), $json);
/** update status */
$payment = $this->updatePaymentStatus(new Payment(), $data);
$payment_provider = $this->setProviderData(new PaymentProviderZakatfpx(), $data);
if (!empty($data->AFCode)) { # create another duplicate record on affiliate
$radiuz_payment_provider = $this->setRadiuzProviderData(new RadiuzPaymentProviderZakatfpx, $payment_provider);
$this->setRadiuzData(new PaymentRadiuz, $payment, $radiuz_payment_provider);
}
/** redirect to receipt page */
$base = self::BASE_CROWD_URL();
$success_url = $base . self::SUCCESS_URL() . '?h=' . $payment->payment_hash;
$failed_url = $base . self::FAILED_URL() . '?h=' . $payment->payment_hash;
if ($data->approvecode === ZakatFpx::SUCCESS_PAYMENT_CODE) {
die(<<<HTML
<meta http-equiv="refresh" content="0;URL='$success_url'" />
HTML);
}
die(<<<HTML
<meta http-equiv="refresh" content="0;URL='$failed_url'" />
HTML);
break;
case $prefix === self::RADIUZ_ZAKAT_PREFIX:
/** save response */
$this->saveResponse(new RadiuzCallback(), $json);
/** update status */
$payment = $this->updatePaymentStatus(new RadiuzPayment(), $data);
$this->setProviderData(new RadiuzPaymentProviderZakatfpx(), $data);
/** redirect to receipt page */
$base = Yii::$app->params['payment_gateway']['zakat_fpx']['response']['base_url']['radiuz'];
$success_url = $base . self::SUCCESS_URL() . '?h=' . $payment->payment_hash;
$failed_url = $base . self::FAILED_URL() . '?h=' . $payment->payment_hash;
if ($data->approvecode === ZakatFpx::SUCCESS_PAYMENT_CODE) {
die(<<<HTML
<meta http-equiv="refresh" content="0;URL='$success_url'" />
HTML);
}
die(<<<HTML
<meta http-equiv="refresh" content="0;URL='$failed_url'" />
HTML);
break;
default:
throw new \Exception('Invalid number prefix received');
break;
}
} catch (\Throwable $t) {
Yii::$app->response->setStatusCode(500);
(new ErrorHandler)->logException($t);
return $this->asJson([
'success' => false,
'message' => $t->getMessage()
]);
}
}
/**
* Save response get from payment gateway
* @param PaymentCallback $call Payment callback model object
* @param string $json Json receive from payment gateway
* @return void return to the url receipt
*/
private function saveResponse($call, $json)
{
$call->post = $json;
$call->url = Url::to();
$call->get = json_encode(Yii::$app->request->get());
if (!$call->save()) {
throw new \Exception("Error saving response callback");
}
}
/**
* Update payment status, and insert payment information
* @param Payment $payment Payment model
* @param Json $data Json object
* @return Payment
*/
private function updatePaymentStatus($payment, $data)
{
$payment_model = $payment::find()
->where(['payment_gateway_refno' => $data->myTransID])
->one();
if (empty($payment_model)) {
throw new \Exception("Error update payment status, order number is invalid");
}
$payment_model->payment_status = 'failed';
$payment_model->updated_at = date('Y-m-d H:i:s');
# common
$payment_model->payment_gateway_status = $data->status ?? '';
$payment_model->payment_gateway_code = $data->approvecode ?? '';
# return verified
if (ZakatFpx::getStatus($data->approvecode) === true) {
$d = \DateTime::createFromFormat("d/m/Y H:i:s A", $data->transDate);
$c = \DateTime::createFromFormat("d/m/Y H:i:s A", $data->dateCreated);
$payment_model->created_at = $c->format('Y-m-d H:i:s');
$payment_model->payment_status = 'verified';
$payment_model->payment_gateway_code = $data->approvecode;
$payment_model->payment_gateway_status = $data->status;
$payment_model->payment_approved_at = date('Y-m-d H:i:s');
$payment_model->payment_bank = $data->buyerbank ?? '';
$payment_model->payment_affiliate_code = $data->AFCode;
$payment_model->payment_gateway_order_id = $data->fpxOrderNo ?? '';
$payment_model->payment_gateway_transaction_time = $d->format("Y-m-d H:i:s");
$payment_model->payment_gateway_transaction_id = $data->fpxtransacid;
$payment_model->payment_receipt_no = $this->generateReceiptNumber($payment);
}
if (!$payment_model->save()) {
throw new \Exception("Error update payment status");
}
return $payment_model;
}
private function setProviderData($provider, $data)
{
# only save record if the response is success
if (empty($data->approvecode)) {
return;
}
$payment_provider = $provider;
$update_provider = $provider::find()
->where(['order_number' => $data->myTransID])
->one();
if (!empty($update_provider)) {
# throw new \Exception("Error update provider data");
$payment_provider = $update_provider;
}
$d = \DateTime::createFromFormat("d/m/Y H:i:s A", $data->transDate);
$payment_provider->bank = $data->buyerbank;
$payment_provider->name = $data->name;
$payment_provider->email = $data->email;
$payment_provider->fpx_order_no = $data->fpxOrderNo;
$payment_provider->transaction_date = $d->format("Y-m-d H:i:s");
$payment_provider->amount = $data->amountZakat;
$payment_provider->approve_amount = $data->apprvamount;
$payment_provider->type = $data->desZakat;
$payment_provider->approve_code = $data->approvecode;
$payment_provider->transaction_id = $data->fpxtransacid;
$payment_provider->buyer_bank = $data->buyerbank;
$payment_provider->payer_status = $data->payerstatus;
$payment_provider->order_number = $data->myTransID;
$payment_provider->status = $data->status;
$payment_provider->afcode = $data->AFCode ?? null;
if (!$payment_provider->save()) {
throw new \Exception("Error update payment status");
}
return $payment_provider;
}
private function setRadiuzData($provider, $payment, $carry_provider) // carry provider -> radiuz zakat fpx provider table
{
$payment_provider = $provider;
$update_provider = $provider::find()
->where(['payment_gateway_refno' => $payment->payment_gateway_refno])
->one();
if (!empty($update_provider)) {
$payment_provider = $update_provider;
}
$exclude = ['payment_id', 'payment_provider_id'];
# Set the payment channel
$payment_provider->payment_channel = 'CROWDZAKAT';
$payment_provider->payment_provider_id = $carry_provider->zakatfpx_id; #grab zakat provider id
# Loop trough each attribute and set it
foreach (array_keys($payment->attributes) as $attrib) {
if (in_array($attrib, $exclude)) {
continue; # skip any excluded attribute
}
if (in_array($attrib, array_keys($payment_provider->attributes))) {
$payment_provider->{$attrib} = $payment->{$attrib};
}
}
if (!$payment_provider->save()) {
throw new \Exception('Error Saving Payment to Radiuz' . json_encode($payment_provider->errors));
}
return;
}
private function setRadiuzProviderData($provider, $payment)
{
$payment_provider = $provider;
$update_provider = $provider::find()
->where(['order_number' => $payment->order_number])
->one();
if (!empty($update_provider)) {
$payment_provider = $update_provider;
}
$exclude = ['zakatfpx_id'];
# Loop trough each attribute and set it
foreach (array_keys($payment->attributes) as $attrib) {
if (in_array($attrib, $exclude)) {
continue; # skip any excluded attribute
}
if (in_array($attrib, array_keys($payment_provider->attributes))) {
$payment_provider->{$attrib} = $payment->{$attrib};
}
}
if (!$payment_provider->save()) {
throw new \Exception('Error Saving Payment Provider to Radiuz' . json_encode($payment_provider->errors));
}
return $payment_provider;
}
private function generateReceiptNumber($payment)
{
return ZakatFpx::generateReceiptNumber(
$payment::find()
->where('LENGTH(payment_receipt_no) = 14')
->max(
<<<SQL
CONCAT(
SUBSTRING(payment_receipt_no, 1, 6),
SUBSTRING(payment_receipt_no, 9, 5)
)
SQL
)
);
}
public function actionTest($channel, $transaction_id)
{
$zakatfpx = new \common\components\payment\provider\ZakatFpx();
$token = $zakatfpx->getTransactionDetail($channel, $transaction_id);
// var_dump($token);
}
}