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

Added void action for transactions #1480

Closed
wants to merge 1 commit into from
Closed
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
5 changes: 4 additions & 1 deletion src/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public static function t($message, $params = [], $language = null)
* @inheritDoc
*/

public $schemaVersion = '3.1.11';
public $schemaVersion = '3.1.12';

/**
* @inheritdoc
Expand Down Expand Up @@ -362,6 +362,9 @@ private function _registerPermissions()
'commerce-capturePayment' => [
'label' => self::t('Capture payment')
],
'commerce-voidPayment' => [
'label' => self::t('Void payment')
],
'commerce-refundPayment' => [
'label' => self::t('Refund payment')
],
Expand Down
71 changes: 58 additions & 13 deletions src/controllers/OrdersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,44 @@ public function actionTransactionCapture(): Response
return $this->redirectToPostedUrl();
}

/**
* Voids Transaction
*
* @return Response
* @throws TransactionException
* @throws MissingComponentException
* @throws BadRequestHttpException
*/
public function actionTransactionVoid(): Response
{
$this->requirePermission('commerce-VoidPayment');
$this->requirePostRequest();
$id = Craft::$app->getRequest()->getRequiredBodyParam('id');
$transaction = Plugin::getInstance()->getTransactions()->getTransactionById($id);

if ($transaction->canVoid()) {
// void transaction and display result
$child = Plugin::getInstance()->getPayments()->voidTransaction($transaction);

$message = $child->message ? ' (' . $child->message . ')' : '';

if ($child->status == TransactionRecord::STATUS_SUCCESS) {
$child->order->updateOrderPaidInformation();
Craft::$app->getSession()->setNotice(Plugin::t('Transaction voided successfully: {message}', [
'message' => $message
]));
} else {
Craft::$app->getSession()->setError(Plugin::t('Couldn’t void transaction: {message}', [
'message' => $message
]));
}
} else {
Craft::$app->getSession()->setError(Plugin::t('Couldn’t void transaction.', ['id' => $id]));
}

return $this->redirectToPostedUrl();
}

/**
* Refunds transaction.
*
Expand Down Expand Up @@ -1300,19 +1338,26 @@ private function _getTransactionsWIthLevelsTableArray($transactions, $level = 0)
$user = Craft::$app->getUser()->getIdentity();
foreach ($transactions as $transaction) {
if (!ArrayHelper::firstWhere($return, 'id', $transaction->id)) {
$refundCapture = '';
if ($user->can('commerce-capturePayment') && $transaction->canCapture()) {
$refundCapture = Craft::$app->getView()->renderTemplate(
'commerce/orders/includes/_capture',
[
'currentUser' => $user,
'transaction' => $transaction,
]
);
} else if ($user->can('commerce-refundPayment') && $transaction->canRefund()) {
$refundCapture = Craft::$app->getView()->renderTemplate(
'commerce/orders/includes/_refund',
$refundCaptureVoid = '';
$actions = [];

if ($user->can('commerce-capturePayment') && $canCapture = $transaction->canCapture()) {
$actions[] = 'capture';
}

if ($user->can('commerce-voidPayment') && $canVoid = $transaction->canVoid()) {
$actions[] = 'void';
}

if (!$canCapture && !$canVoid && $user->can('commerce-refundPayment') && $transaction->canRefund()) {
$actions[] = 'refund';
}

if (!empty($actions)) {
$refundCaptureVoid = Craft::$app->getView()->renderTemplate(
'commerce/orders/includes/_refundCaptureVoid',
[
'actions' => $actions,
'currentUser' => $user,
'transaction' => $transaction,
]
Expand Down Expand Up @@ -1349,7 +1394,7 @@ private function _getTransactionsWIthLevelsTableArray($transactions, $level = 0)
['label' => Html::encode(Plugin::t('Converted Price')), 'type' => 'text', 'value' => Plugin::getInstance()->getPaymentCurrencies()->convert($transaction->paymentAmount, $transaction->paymentCurrency) . ' <small class="light">(' . $transaction->currency . ')</small>' . ' <small class="light">(1 ' . $transaction->currency . ' = ' . number_format($transaction->paymentRate) . ' ' . $transaction->paymentCurrency . ')</small>'],
['label' => Html::encode(Plugin::t('Gateway Response')), 'type' => 'response', 'value' => $transactionResponse],
],
'actions' => $refundCapture,
'actions' => $refundCaptureVoid,
];

if (!empty($transaction->childTransactions)) {
Expand Down
43 changes: 43 additions & 0 deletions src/migrations/m200528_201030_add_void.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace craft\commerce\migrations;

use Craft;
use craft\db\Migration;

/**
* m200528_201030_add_void migration.
*/
class m200528_201030_add_void extends Migration
{
/**
* @inheritdoc
*/
public function safeUp()
{
$values = ['authorize', 'capture', 'purchase', 'refund', 'void'];
if ($this->db->getIsPgsql()) {
// Manually construct the SQL for Postgres
$check = '[[type]] in (';
foreach ($values as $i => $value) {
if ($i != 0) {
$check .= ',';
}
$check .= $this->db->quoteValue($value);
}
$check .= ')';
$this->execute("alter table {{%commerce_transactions}} drop constraint {{%commerce_transactions_type_check}}, add check ({$check})");
} else {
$this->alterColumn('{{%commerce_transactions}}', 'type', $this->enum('type', $values));
}
}

/**
* @inheritdoc
*/
public function safeDown()
{
echo "m200528_201030_add_void cannot be reverted.\n";
return false;
}
}
8 changes: 8 additions & 0 deletions src/models/Transaction.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,14 @@ public function canCapture(): bool
return Plugin::getInstance()->getTransactions()->canCaptureTransaction($this);
}

/**
* @return bool
*/
public function canVoid(): bool
{
return Plugin::getInstance()->getTransactions()->canVoidTransaction($this);
}

/**
* @return bool
*/
Expand Down
1 change: 1 addition & 0 deletions src/records/Transaction.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class Transaction extends ActiveRecord
{
const TYPE_AUTHORIZE = 'authorize';
const TYPE_CAPTURE = 'capture';
const TYPE_VOID = 'void';
const TYPE_PURCHASE = 'purchase';
const TYPE_REFUND = 'refund';
const STATUS_PENDING = 'pending';
Expand Down
107 changes: 105 additions & 2 deletions src/services/Payments.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,54 @@ class Payments extends Component
*/
const EVENT_AFTER_CAPTURE_TRANSACTION = 'afterCaptureTransaction';

/**
* @event TransactionEvent The event that is triggered before a payment transaction is voided.
*
* ```php
* use craft\commerce\events\TransactionEvent;
* use craft\commerce\services\Payments;
* use craft\commerce\models\Transaction;
* use yii\base\Event;
*
* Event::on(
* Payments::class,
* Payments::EVENT_BEFORE_VOID_TRANSACTION,
* function(TransactionEvent $event) {
* // @var Transaction $transaction
* $transaction = $event->transaction;
*
* // Check that order isn't already processing
* // ...
* }
* );
* ```
*/
const EVENT_BEFORE_VOID_TRANSACTION = 'beforeVoidTransaction';

/**
* @event TransactionEvent The event that is triggered after a payment transaction is voided.
*
* ```php
* use craft\commerce\events\TransactionEvent;
* use craft\commerce\services\Payments;
* use craft\commerce\models\Transaction;
* use yii\base\Event;
*
* Event::on(
* Payments::class,
* Payments::EVENT_AFTER_VOID_TRANSACTION,
* function(TransactionEvent $event) {
* // @var Transaction $transaction
* $transaction = $event->transaction;
*
* // Notify customer their transaction has been voided
* // ...
* }
* );
* ```
*/
const EVENT_AFTER_VOID_TRANSACTION = 'afterVoidTransaction';

/**
* @event TransactionEvent The event that is triggered before a transaction is refunded.
*
Expand Down Expand Up @@ -346,6 +394,34 @@ public function captureTransaction(Transaction $transaction): Transaction
return $transaction;
}

/**
* Void a transaction.
*
* @param Transaction $transaction the transaction to void.
* @return Transaction
* @throws TransactionException if something went wrong when saving the transaction
*/
public function voidTransaction(Transaction $transaction): Transaction
{
// Raise 'beforeVoidTransaction' event
if ($this->hasEventHandlers(self::EVENT_BEFORE_VOID_TRANSACTION)) {
$this->trigger(self::EVENT_BEFORE_VOID_TRANSACTION, new TransactionEvent([
'transaction' => $transaction
]));
}

$transaction = $this->_void($transaction);

// Raise 'afterVoidTransaction' event
if ($this->hasEventHandlers(self::EVENT_AFTER_VOID_TRANSACTION)) {
$this->trigger(self::EVENT_AFTER_VOID_TRANSACTION, new TransactionEvent([
'transaction' => $transaction
]));
}

return $transaction;
}

/**
* Refund a transaction.
*
Expand Down Expand Up @@ -582,7 +658,7 @@ private function _handleRedirect(RequestResponseInterface $response, &$redirect)
}

/**
* Process a capture or refund exception.
* Process a capture transaction.
*
* @param Transaction $parent
* @return Transaction
Expand All @@ -609,7 +685,34 @@ private function _capture(Transaction $parent): Transaction
}

/**
* Process a capture or refund exception.
* Process a void transaction.
*
* @param Transaction $parent
* @return Transaction
* @throws TransactionException if unable to save transaction
*/
private function _void(Transaction $parent): Transaction
{
$child = Plugin::getInstance()->getTransactions()->createTransaction(null, $parent, TransactionRecord::TYPE_VOID);

$gateway = $parent->getGateway();

try {
$response = $gateway->void($child, (string)$parent->reference);
$this->_updateTransaction($child, $response);
} catch (Exception $e) {
$child->status = TransactionRecord::STATUS_FAILED;
$child->message = $e->getMessage();
$this->_saveTransaction($child);

Craft::error($e->getMessage());
}

return $child;
}

/**
* Process a refund transaction.
*
* @param Transaction $parent
* @param float|null $amount
Expand Down
Loading