Skip to content

Instantly share code, notes, and snippets.

@jasminsuljic
Created October 31, 2019 08:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jasminsuljic/5955c8ed715bee6060c2208626a55bcc to your computer and use it in GitHub Desktop.
Save jasminsuljic/5955c8ed715bee6060c2208626a55bcc to your computer and use it in GitHub Desktop.
<?php
namespace app\controllers;
use http\Exception\InvalidArgumentException;
use Yii;
use yii\base\Security;
use yii\helpers\Json;
use yii\helpers\Url;
use yii\helpers\VarDumper;
use yii\web\Controller;
use yii\web\HttpException;
use yii\web\Response;
class ExampleController extends Controller
{
var $url;
var $appUrl;
public function beforeAction($action)
{
\Yii::$app->response->format = Response::FORMAT_JSON;
return parent::beforeAction($action);
}
public function init()
{
parent::init();
// Test env url -> https://ipgtest.monri.com
// Prod env url -> https://ipg.monri.com
$this->url = Yii::$app->params['baseUrlTest'];
// Your application url
$this->appUrl = !isset(Yii::$app->params['appUrl']) ? "https://mobile.webteh.hr" : Yii::$app->params['appUrl'];
}
/**
* @return array
* @throws HttpException
*/
public function actionOrder()
{
$this->checkIfPostRequest();
$transactionData = $this->transactionData('purchase');
$transactionAuthorizationResponse = $this->transactionAuthorization($this->url, $transactionData);
if ($transactionAuthorizationResponse['status'] == 'declined') {
return [
'status' => 'declined',
'response' => $transactionAuthorizationResponse
];
}
$response = $transactionAuthorizationResponse['response'];
if ($this->isSecureMessageResponse($response)) {
return $this->userActionRequired($transactionData['order_number'], $response['secure_message']);
} else if ($this->isTransactionResponse($response)) {
$transaction = $response['transaction'];
$this->updateOrder($transaction);
return [
'status' => self::statusFromTrxResponse($transaction),
'transaction' => $transaction
];
} else {
// This should never happen
return [
'status' => 'declined',
'transaction' => $response
];
}
}
function isSecureMessageResponse($response)
{
return $response && isset($response['secure_message']);
}
function isTransactionResponse($response)
{
return $response && isset($response['transaction']);
}
function threeDsFormUrl($id)
{
return Url::to($this->appUrl . "/example/three-ds-form?id=$id");
}
function threeDSForm($data)
{
$str = <<<HTML
<!DOCTYPE html>
<html>
<head>
<title>3D Secure Verification</title>
<script language="Javascript">
function OnLoadEvent() {
document.form.submit();
}
</script>
</head>
<body OnLoad="OnLoadEvent();">
Invoking 3-D secure form, please wait ...
<form name="form" action="{$data['acs_url']}" method="post">
<input type="hidden" name="PaReq" value="{$data['pareq']}">
<input type="hidden" name="TermUrl" value="{$data['term_url']}">
<input type="hidden" name="MD" value="{$data['authenticity_token']}">
<noscript>
<p>Please click</p>
<input id="to-acs-button" type="submit">
</noscript>
</form>
</body>
</html>
HTML;
return $str;
}
function transactionData($transactionType)
{
$amount = '100';
$sec = new Security();
$currency = 'EUR';
$order_number = Yii::$app->request->post('order_number');
$key = 'TestKeyXULLyvgWyPJSwOHe'; // TODO: replace with your merchant's key
$authenticity_token = '6a13d79bde8da9320e88923cb3472fb638619ccb'; // TODO: replace with your merchant's authenticity token
//digest = SHA512(key + order_number + amount + currency)
$digest = hash('sha512', $key . $order_number . $amount . $currency);
return [
"transaction_type" => $transactionType,
"amount" => $amount,
"ip" => '10.1.10.111',
'order_info' => 'Monri components trx',
'ch_address' => 'Adresa',
'ch_city' => 'Grad',
'ch_country' => 'BIH',
'ch_email' => 'test@test.com',
'ch_full_name' => 'Test',
'ch_phone' => '061 000 000',
'ch_zip' => '71000',
'currency' => $currency,
'digest' => $digest,
'order_number' => $order_number,
'authenticity_token' => $authenticity_token,
'language' => 'en',
// This part is important! Extract monriToken from post body
'temp_card_id' => Yii::$app->request->post('monriToken')
];
}
public function actionThreeDsForm()
{
Yii::$app->response->format = Response::FORMAT_HTML;
return $this->threeDSForm(Yii::$app->cache->get(Yii::$app->request->get('id')));
}
private static function statusFromTrxResponse($response)
{
$approved = $response['status'] === 'approved' && $response['response_code'] === '0000';
return $approved ? 'approved' : 'declined';
}
function updateOrder($transaction)
{
$status = self::statusFromTrxResponse($transaction);
if ('approved' === $status) {
// execute order
// Save in db
// deliver goods
} else {
// reverse | cancel order
// Save in db
}
}
/**
* @throws HttpException
*/
public function checkIfPostRequest()
{
if (!\Yii::$app->request->isPost) {
throw new HttpException(405);
}
}
private function userActionRequired($order_number, $secure_message)
{
\Yii::$app->cache->set($order_number, [
"acs_url" => $secure_message['acs_url'],
"pareq" => $secure_message['acs_url'],
"authenticity_token" => $secure_message['authenticity_token'],
'term_url' => Url::to($this->appUrl . "/example/three-ds-result?id=$order_number")
]);
return [
'status' => 'action_required',
'action' => [
'redirect_to' => $this->threeDsFormUrl($order_number)
]
];
}
/**
* @param $id
* @throws HttpException
*/
public function actionThreeDsResult($id)
{
$this->checkIfPostRequest();
$data = Yii::$app->request->post();
$paresResponse = $this->pares($this->url, $data);
Yii::$app->cache->delete($id);
if ($paresResponse['status'] == 'declined') {
$this->redirect("monriapp://example.monri.com/transaction-result?order_number=$id");
} else {
$response = $paresResponse['response'];
if ($this->isTransactionResponse($response)) {
$transaction = $response['transaction'];
$this->updateOrder($transaction);
} else {
// This should not happen
}
$this->redirect("monriapp://example.monri.com/transaction-result?order_number=$id");
}
}
function transactionAuthorization($url, $data)
{
$result = '';
try {
/** @noinspection DuplicatedCode */
$data_string = Json::encode(['transaction' => $data]);
// Execute transaction
$ch = curl_init($url . '/v2/transaction');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string))
);
$result = curl_exec($ch);
if (curl_errno($ch)) {
return ['response' => null, 'status' => 'declined', 'error' => curl_error($ch)];
}
curl_close($ch);
return ['response' => Json::decode($result), 'status' => 'approved'];
} catch (\Exception $exception) {
return ['response' => null, 'status' => 'declined', 'result' => $result, 'url' => $url];
}
}
function pares($url, $data)
{
$data_string = Json::encode($data);
$ch = curl_init($url . '/v2/pares');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Content-Length: ' . strlen($data_string))
);
$result = curl_exec($ch);
$result = curl_exec($ch);
if (curl_errno($ch)) {
return ['response' => null, 'status' => 'declined', 'error' => curl_error($ch)];
}
return ['response' => Json::decode($result), 'status' => 'approved'];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment