<?php namespace Shetabit\Multipay\Drivers\Zibal; use GuzzleHttp\Client; use Shetabit\Multipay\Abstracts\Driver; use Shetabit\Multipay\Exceptions\InvalidPaymentException; use Shetabit\Multipay\Exceptions\PurchaseFailedException; use Shetabit\Multipay\Contracts\ReceiptInterface; use Shetabit\Multipay\Invoice; use Shetabit\Multipay\Receipt; use Shetabit\Multipay\RedirectionForm; use Shetabit\Multipay\Request; class Zibal extends Driver { /** * Zibal Client. * * @var object */ protected $client; /** * Invoice * * @var Invoice */ protected $invoice; /** * Driver settings * * @var object */ protected $settings; /** * Zibal constructor. * Construct the class with the relevant settings. * * @param Invoice $invoice * @param $settings */ public function __construct(Invoice $invoice, $settings) { $this->invoice($invoice); $this->settings = (object) $settings; $this->client = new Client(); } /** * Purchase Invoice. * * @return string * * @throws \GuzzleHttp\Exception\GuzzleException */ public function purchase() { $details = $this->invoice->getDetails(); $amount = $this->invoice->getAmount() * ($this->settings->currency == 'T' ? 10 : 1); // convert to rial $orderId = crc32($this->invoice->getUuid()).time(); if (!empty($details['orderId'])) { $orderId = $details['orderId']; } elseif (!empty($details['order_id'])) { $orderId = $details['order_id']; } $data = array( "merchant"=> $this->settings->merchantId, //required "callbackUrl"=> $this->settings->callbackUrl, //required "amount"=> $amount, //required "orderId"=> $orderId, //optional ); // Pass current $data array to add existing optional details $data = $this->checkOptionalDetails($data); $response = $this->client->request( 'POST', $this->settings->apiPurchaseUrl, ["json" => $data, "http_errors" => false] ); $body = json_decode($response->getBody()->getContents(), false); if ($body->result != 100) { // some error has happened throw new PurchaseFailedException($body->message); } $this->invoice->transactionId($body->trackId); // return the transaction's id return $this->invoice->getTransactionId(); } /** * Pay the Invoice * * @return RedirectionForm */ public function pay() : RedirectionForm { $payUrl = $this->settings->apiPaymentUrl.$this->invoice->getTransactionId(); if (strtolower($this->settings->mode) == 'direct') { $payUrl .= '/direct'; } return $this->redirectWithForm($payUrl); } /** * Verify payment * * @return mixed|void * * @throws InvalidPaymentException * @throws \GuzzleHttp\Exception\GuzzleException */ public function verify() : ReceiptInterface { $successFlag = Request::input('success'); $status = Request::input('status'); $orderId = Request::input('orderId'); $transactionId = $this->invoice->getTransactionId() ?? Request::input('trackId'); if ($successFlag != 1) { $this->notVerified($this->translateStatus($status), $status); } //start verfication $data = array( "merchant" => $this->settings->merchantId, //required "trackId" => $transactionId, //required ); $response = $this->client->request( 'POST', $this->settings->apiVerificationUrl, ["json" => $data, "http_errors" => false] ); $body = json_decode($response->getBody()->getContents(), false); if ($body->result != 100) { $this->notVerified($body->message, $body->result); } /* for more info: var_dump($body); */ return $this->createReceipt($body->refNumber); } /** * Generate the payment's receipt * * @param $referenceId * * @return Receipt */ protected function createReceipt($referenceId) { $receipt = new Receipt('Zibal', $referenceId); return $receipt; } private function translateStatus($status) { $translations = [ -2 => 'خطای داخلی', -1 => 'در انتظار پردخت', 2 => 'پرداخت شده - تاییدنشده', 3 => 'تراکنش توسط کاربر لغو شد.', 4 => 'شماره کارت نامعتبر میباشد.', 5 => 'موجودی حساب کافی نمیباشد.', 6 => 'رمز واردشده اشتباه میباشد.', 7 => 'تعداد درخواستها بیش از حد مجاز میباشد.', 8 => 'تعداد پرداخت اینترنتی روزانه بیش از حد مجاز میباشد.', 9 => 'مبلغ پرداخت اینترنتی روزانه بیش از حد مجاز میباشد.', 10 => 'صادرکنندهی کارت نامعتبر میباشد.', 11 => 'خطای سوییچ', 12 => 'کارت قابل دسترسی نمیباشد.' ]; $unknownError = 'خطای ناشناخته ای رخ داده است.'; return array_key_exists($status, $translations) ? $translations[$status] : $unknownError; } /** * Trigger an exception * * @param $message * @throws InvalidPaymentException */ private function notVerified($message, $code = 0) { if (empty($message)) { throw new InvalidPaymentException('خطای ناشناخته ای رخ داده است.', $code); } else { throw new InvalidPaymentException($message, $code); } } /** * Retrieve data from details using its name. * * @return string */ private function extractDetails($name) { $detail = null; if (!empty($this->invoice->getDetails()[$name])) { $detail = $this->invoice->getDetails()[$name]; } elseif (!empty($this->settings->$name)) { $detail = $this->settings->$name; } return $detail; } /** * Checks optional parameters existence (except orderId) and * adds them to the given $data array and returns new array * with optional parameters for api call. * * To avoid errors and have a cleaner api call log, `null` * parameters are not sent. * * To add new parameter support in the future, all that * is needed is to add parameter name to $optionalParameters * array. * * @param $data * * @return array */ private function checkOptionalDetails($data) { $optionalParameters = [ 'mobile', 'description', 'allowedCards', 'feeMode', 'percentMode', 'multiplexingInfos' ]; foreach ($optionalParameters as $parameter) { if (!is_null($this->extractDetails($parameter))) { $parameterArray = array( $parameter => $this->extractDetails($parameter) ); $data = array_merge($data, $parameterArray); } } return $data; } }