mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-23 20:00:33 -04:00
Better Braintree support
This commit is contained in:
parent
d27822e17b
commit
cf98c37f40
22
app/Events/PaymentWasVoided.php
Normal file
22
app/Events/PaymentWasVoided.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php namespace App\Events;
|
||||||
|
|
||||||
|
use App\Events\Event;
|
||||||
|
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class PaymentWasVoided extends Event {
|
||||||
|
|
||||||
|
use SerializesModels;
|
||||||
|
public $payment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new event instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct($payment)
|
||||||
|
{
|
||||||
|
$this->payment = $payment;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -409,8 +409,9 @@ if (!defined('CONTACT_EMAIL')) {
|
|||||||
//define('ACTIVITY_TYPE_UPDATE_PAYMENT', 11);
|
//define('ACTIVITY_TYPE_UPDATE_PAYMENT', 11);
|
||||||
define('ACTIVITY_TYPE_ARCHIVE_PAYMENT', 12);
|
define('ACTIVITY_TYPE_ARCHIVE_PAYMENT', 12);
|
||||||
define('ACTIVITY_TYPE_DELETE_PAYMENT', 13);
|
define('ACTIVITY_TYPE_DELETE_PAYMENT', 13);
|
||||||
define('ACTIVITY_TYPE_REFUNDED_PAYMENT', 39);
|
define('ACTIVITY_TYPE_VOIDED_PAYMENT', 39);
|
||||||
define('ACTIVITY_TYPE_FAILED_PAYMENT', 40);
|
define('ACTIVITY_TYPE_REFUNDED_PAYMENT', 40);
|
||||||
|
define('ACTIVITY_TYPE_FAILED_PAYMENT', 41);
|
||||||
|
|
||||||
define('ACTIVITY_TYPE_CREATE_CREDIT', 14);
|
define('ACTIVITY_TYPE_CREATE_CREDIT', 14);
|
||||||
//define('ACTIVITY_TYPE_UPDATE_CREDIT', 15);
|
//define('ACTIVITY_TYPE_UPDATE_CREDIT', 15);
|
||||||
@ -489,10 +490,11 @@ if (!defined('CONTACT_EMAIL')) {
|
|||||||
define('INVOICE_STATUS_PAID', 6);
|
define('INVOICE_STATUS_PAID', 6);
|
||||||
|
|
||||||
define('PAYMENT_STATUS_PENDING', 1);
|
define('PAYMENT_STATUS_PENDING', 1);
|
||||||
define('PAYMENT_STATUS_FAILED', 2);
|
define('PAYMENT_STATUS_VOIDED', 2);
|
||||||
define('PAYMENT_STATUS_COMPLETED', 3);
|
define('PAYMENT_STATUS_FAILED', 3);
|
||||||
define('PAYMENT_STATUS_PARTIALLY_REFUNDED', 4);
|
define('PAYMENT_STATUS_COMPLETED', 4);
|
||||||
define('PAYMENT_STATUS_REFUNDED', 5);
|
define('PAYMENT_STATUS_PARTIALLY_REFUNDED', 5);
|
||||||
|
define('PAYMENT_STATUS_REFUNDED', 6);
|
||||||
|
|
||||||
define('CUSTOM_DESIGN', 11);
|
define('CUSTOM_DESIGN', 11);
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ use App\Events\QuoteInvitationWasApproved;
|
|||||||
use App\Events\PaymentWasCreated;
|
use App\Events\PaymentWasCreated;
|
||||||
use App\Events\PaymentWasDeleted;
|
use App\Events\PaymentWasDeleted;
|
||||||
use App\Events\PaymentWasRefunded;
|
use App\Events\PaymentWasRefunded;
|
||||||
|
use App\Events\PaymentWasVoided;
|
||||||
use App\Events\PaymentWasArchived;
|
use App\Events\PaymentWasArchived;
|
||||||
use App\Events\PaymentWasRestored;
|
use App\Events\PaymentWasRestored;
|
||||||
use App\Events\PaymentFailed;
|
use App\Events\PaymentFailed;
|
||||||
@ -323,6 +324,18 @@ class ActivityListener
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function voidedPayment(PaymentWasVoided $event)
|
||||||
|
{
|
||||||
|
$payment = $event->payment;
|
||||||
|
|
||||||
|
$this->activityRepo->create(
|
||||||
|
$payment,
|
||||||
|
ACTIVITY_TYPE_VOIDED_PAYMENT,
|
||||||
|
$payment->amount,
|
||||||
|
$payment->amount * -1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function failedPayment(PaymentFailed $event)
|
public function failedPayment(PaymentFailed $event)
|
||||||
{
|
{
|
||||||
$payment = $event->payment;
|
$payment = $event->payment;
|
||||||
|
@ -9,6 +9,7 @@ use App\Events\PaymentWasCreated;
|
|||||||
use App\Events\PaymentWasDeleted;
|
use App\Events\PaymentWasDeleted;
|
||||||
use App\Events\PaymentWasRefunded;
|
use App\Events\PaymentWasRefunded;
|
||||||
use App\Events\PaymentWasRestored;
|
use App\Events\PaymentWasRestored;
|
||||||
|
use App\Events\PaymentWasVoided;
|
||||||
use App\Events\PaymentFailed;
|
use App\Events\PaymentFailed;
|
||||||
use App\Events\InvoiceInvitationWasViewed;
|
use App\Events\InvoiceInvitationWasViewed;
|
||||||
|
|
||||||
@ -76,6 +77,16 @@ class InvoiceListener
|
|||||||
$invoice->updatePaidStatus();
|
$invoice->updatePaidStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function voidedPayment(PaymentWasVoided $event)
|
||||||
|
{
|
||||||
|
$payment = $event->payment;
|
||||||
|
$invoice = $payment->invoice;
|
||||||
|
$adjustment = $payment->amount;
|
||||||
|
|
||||||
|
$invoice->updateBalances($adjustment);
|
||||||
|
$invoice->updatePaidStatus();
|
||||||
|
}
|
||||||
|
|
||||||
public function failedPayment(PaymentFailed $event)
|
public function failedPayment(PaymentFailed $event)
|
||||||
{
|
{
|
||||||
$payment = $event->payment;
|
$payment = $event->payment;
|
||||||
|
@ -4,7 +4,9 @@ use Event;
|
|||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use App\Events\PaymentWasCreated;
|
use App\Events\PaymentWasCreated;
|
||||||
use App\Events\PaymentWasRefunded;
|
use App\Events\PaymentWasRefunded;
|
||||||
|
use App\Events\PaymentWasVoided;
|
||||||
use App\Events\PaymentCompleted;
|
use App\Events\PaymentCompleted;
|
||||||
|
use App\Events\PaymentVoided;
|
||||||
use App\Events\PaymentFailed;
|
use App\Events\PaymentFailed;
|
||||||
use Laracasts\Presenter\PresentableTrait;
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
|
|
||||||
@ -90,7 +92,7 @@ class Payment extends EntityModel
|
|||||||
|
|
||||||
public function isCompleted()
|
public function isCompleted()
|
||||||
{
|
{
|
||||||
return $this->payment_status_id >= PAYMENT_STATUS_COMPLETED;
|
return $this->payment_status_id == PAYMENT_STATUS_COMPLETED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isPartiallyRefunded()
|
public function isPartiallyRefunded()
|
||||||
@ -103,9 +105,14 @@ class Payment extends EntityModel
|
|||||||
return $this->payment_status_id == PAYMENT_STATUS_REFUNDED;
|
return $this->payment_status_id == PAYMENT_STATUS_REFUNDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isVoided()
|
||||||
|
{
|
||||||
|
return $this->payment_status_id == PAYMENT_STATUS_VOIDED;
|
||||||
|
}
|
||||||
|
|
||||||
public function recordRefund($amount = null)
|
public function recordRefund($amount = null)
|
||||||
{
|
{
|
||||||
if (!$this->isRefunded()) {
|
if (!$this->isRefunded() && !$this->isVoided()) {
|
||||||
if (!$amount) {
|
if (!$amount) {
|
||||||
$amount = $this->amount;
|
$amount = $this->amount;
|
||||||
}
|
}
|
||||||
@ -123,6 +130,17 @@ class Payment extends EntityModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function markVoided()
|
||||||
|
{
|
||||||
|
if (!$this->isVoided() && !$this->isPartiallyRefunded() && !$this->isRefunded()) {
|
||||||
|
$this->refunded = $this->amount;
|
||||||
|
$this->payment_status_id = PAYMENT_STATUS_VOIDED;
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
Event::fire(new PaymentWasVoided($this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function markComplete()
|
public function markComplete()
|
||||||
{
|
{
|
||||||
$this->payment_status_id = PAYMENT_STATUS_COMPLETED;
|
$this->payment_status_id = PAYMENT_STATUS_COMPLETED;
|
||||||
|
@ -112,6 +112,10 @@ class EventServiceProvider extends ServiceProvider {
|
|||||||
'App\Listeners\InvoiceListener@refundedPayment',
|
'App\Listeners\InvoiceListener@refundedPayment',
|
||||||
'App\Listeners\CreditListener@refundedPayment',
|
'App\Listeners\CreditListener@refundedPayment',
|
||||||
],
|
],
|
||||||
|
'App\Events\PaymentWasVoided' => [
|
||||||
|
'App\Listeners\ActivityListener@voidedPayment',
|
||||||
|
'App\Listeners\InvoiceListener@voidedPayment',
|
||||||
|
],
|
||||||
'App\Events\PaymentFailed' => [
|
'App\Events\PaymentFailed' => [
|
||||||
'App\Listeners\ActivityListener@failedPayment',
|
'App\Listeners\ActivityListener@failedPayment',
|
||||||
'App\Listeners\InvoiceListener@failedPayment',
|
'App\Listeners\InvoiceListener@failedPayment',
|
||||||
|
@ -14,6 +14,7 @@ use App\Models\Account;
|
|||||||
use App\Models\Country;
|
use App\Models\Country;
|
||||||
use App\Models\Client;
|
use App\Models\Client;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
|
use App\Models\AccountGateway;
|
||||||
use App\Http\Controllers\PaymentController;
|
use App\Http\Controllers\PaymentController;
|
||||||
use App\Models\AccountGatewayToken;
|
use App\Models\AccountGatewayToken;
|
||||||
use App\Ninja\Repositories\PaymentRepository;
|
use App\Ninja\Repositories\PaymentRepository;
|
||||||
@ -27,7 +28,8 @@ class PaymentService extends BaseService
|
|||||||
protected $datatableService;
|
protected $datatableService;
|
||||||
|
|
||||||
protected static $refundableGateways = array(
|
protected static $refundableGateways = array(
|
||||||
GATEWAY_STRIPE
|
GATEWAY_STRIPE,
|
||||||
|
GATEWAY_BRAINTREE
|
||||||
);
|
);
|
||||||
|
|
||||||
public function __construct(PaymentRepository $paymentRepo, AccountRepository $accountRepo, DatatableService $datatableService)
|
public function __construct(PaymentRepository $paymentRepo, AccountRepository $accountRepo, DatatableService $datatableService)
|
||||||
@ -212,7 +214,28 @@ class PaymentService extends BaseService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
|
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
|
||||||
|
$response = $gateway->findCustomer($token)->send();
|
||||||
|
$data = $response->getData();
|
||||||
|
|
||||||
|
if (!($data instanceof \Braintree\Customer)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sources = $data->paymentMethods;
|
||||||
|
|
||||||
|
$paymentTypes = Cache::get('paymentTypes');
|
||||||
|
$currencies = Cache::get('currencies');
|
||||||
|
foreach ($sources as $source) {
|
||||||
|
if ($source instanceof \Braintree\CreditCard) {
|
||||||
|
$paymentMethods[] = array(
|
||||||
|
'id' => $source->token,
|
||||||
|
'default' => $source->isDefault(),
|
||||||
|
'type' => $paymentTypes->find($this->parseCardType($source->cardType)),
|
||||||
|
'last4' => $source->last4,
|
||||||
|
'expiration' => $source->expirationYear . '-' . $source->expirationMonth . '-00',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $paymentMethods;
|
return $paymentMethods;
|
||||||
@ -249,7 +272,24 @@ class PaymentService extends BaseService
|
|||||||
return $response->getMessage();
|
return $response->getMessage();
|
||||||
}
|
}
|
||||||
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
|
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
|
||||||
|
// Make sure the source is owned by this client
|
||||||
|
$sources = $this->getClientPaymentMethods($client);
|
||||||
|
$ownsSource = false;
|
||||||
|
foreach ($sources as $source) {
|
||||||
|
if ($source['id'] == $sourceId) {
|
||||||
|
$ownsSource = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$ownsSource) {
|
||||||
|
return 'Unknown source';
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $gateway->deletePaymentMethod(array('token'=>$sourceId))->send();
|
||||||
|
|
||||||
|
if (!$response->isSuccessful()) {
|
||||||
|
return $response->getMessage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -272,7 +312,27 @@ class PaymentService extends BaseService
|
|||||||
'default_card='.$sourceId
|
'default_card='.$sourceId
|
||||||
);
|
);
|
||||||
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
|
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
|
||||||
|
// Make sure the source is owned by this client
|
||||||
|
$sources = $this->getClientPaymentMethods($client);
|
||||||
|
$ownsSource = false;
|
||||||
|
foreach ($sources as $source) {
|
||||||
|
if ($source['id'] == $sourceId) {
|
||||||
|
$ownsSource = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$ownsSource) {
|
||||||
|
return 'Unknown source';
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $gateway->updatePaymentMethod(array(
|
||||||
|
'token' => $sourceId,
|
||||||
|
'makeDefault' => true,
|
||||||
|
))->send();
|
||||||
|
|
||||||
|
if (!$response->isSuccessful()) {
|
||||||
|
return $response->getMessage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -285,11 +345,19 @@ class PaymentService extends BaseService
|
|||||||
if ($customerReference) {
|
if ($customerReference) {
|
||||||
$details['customerReference'] = $customerReference;
|
$details['customerReference'] = $customerReference;
|
||||||
|
|
||||||
|
if ($accountGateway->gateway->id == GATEWAY_STRIPE) {
|
||||||
$customerResponse = $gateway->fetchCustomer(array('customerReference'=>$customerReference))->send();
|
$customerResponse = $gateway->fetchCustomer(array('customerReference'=>$customerReference))->send();
|
||||||
|
|
||||||
if (!$customerResponse->isSuccessful()){
|
if (!$customerResponse->isSuccessful()){
|
||||||
$customerReference = null; // The customer might not exist anymore
|
$customerReference = null; // The customer might not exist anymore
|
||||||
}
|
}
|
||||||
|
} elseif ($accountGateway->gateway->id == GATEWAY_BRAINTREE) {
|
||||||
|
$customer = $gateway->findCustomer($customerReference)->send()->getData();
|
||||||
|
|
||||||
|
if (!($customer instanceof \Braintree\Customer)){
|
||||||
|
$customerReference = null; // The customer might not exist anymore
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($accountGateway->gateway->id == GATEWAY_STRIPE) {
|
if ($accountGateway->gateway->id == GATEWAY_STRIPE) {
|
||||||
@ -599,8 +667,9 @@ class PaymentService extends BaseService
|
|||||||
|
|
||||||
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
|
} elseif ($accountGateway->gateway_id == GATEWAY_BRAINTREE) {
|
||||||
$details['customerId'] = $token;
|
$details['customerId'] = $token;
|
||||||
$customer = $gateway->findCustomer($token)->send();
|
$customer = $gateway->findCustomer($token)->send()->getData();
|
||||||
$details['paymentMethodToken'] = $customer->getData()->paymentMethods[0]->token;
|
$defaultPaymentMethod = $customer->defaultPaymentMethod();
|
||||||
|
$details['paymentMethodToken'] = $defaultPaymentMethod->token;
|
||||||
}
|
}
|
||||||
|
|
||||||
// submit purchase/get response
|
// submit purchase/get response
|
||||||
@ -723,7 +792,7 @@ class PaymentService extends BaseService
|
|||||||
return "javascript:showRefundModal({$model->public_id}, '{$max_refund}', '{$formatted}', '{$symbol}')";
|
return "javascript:showRefundModal({$model->public_id}, '{$max_refund}', '{$formatted}', '{$symbol}')";
|
||||||
},
|
},
|
||||||
function ($model) {
|
function ($model) {
|
||||||
return Auth::user()->can('editByOwner', [ENTITY_PAYMENT, $model->user_id]) && $model->payment_status_id != PAYMENT_STATUS_FAILED &&
|
return Auth::user()->can('editByOwner', [ENTITY_PAYMENT, $model->user_id]) && $model->payment_status_id >= PAYMENT_STATUS_COMPLETED &&
|
||||||
$model->refunded < $model->amount &&
|
$model->refunded < $model->amount &&
|
||||||
(
|
(
|
||||||
($model->transaction_reference && in_array($model->gateway_id , static::$refundableGateways))
|
($model->transaction_reference && in_array($model->gateway_id , static::$refundableGateways))
|
||||||
@ -742,18 +811,18 @@ class PaymentService extends BaseService
|
|||||||
}
|
}
|
||||||
|
|
||||||
$payments = $this->getRepo()->findByPublicIdsWithTrashed($ids);
|
$payments = $this->getRepo()->findByPublicIdsWithTrashed($ids);
|
||||||
|
$successful = 0;
|
||||||
|
|
||||||
foreach ($payments as $payment) {
|
foreach ($payments as $payment) {
|
||||||
if(Auth::user()->can('edit', $payment)){
|
if(Auth::user()->can('edit', $payment)){
|
||||||
if(!empty($params['amount'])) {
|
$amount = !empty($params['amount']) ? floatval($params['amount']) : null;
|
||||||
$this->refund($payment, floatval($params['amount']));
|
if ($this->refund($payment, $amount)) {
|
||||||
} else {
|
$successful++;
|
||||||
$this->refund($payment);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return count($payments);
|
return $successful;
|
||||||
} else {
|
} else {
|
||||||
return parent::bulk($ids, $action);
|
return parent::bulk($ids, $action);
|
||||||
}
|
}
|
||||||
@ -779,6 +848,7 @@ class PaymentService extends BaseService
|
|||||||
]);
|
]);
|
||||||
$class = 'primary';
|
$class = 'primary';
|
||||||
break;
|
break;
|
||||||
|
case PAYMENT_STATUS_VOIDED:
|
||||||
case PAYMENT_STATUS_REFUNDED:
|
case PAYMENT_STATUS_REFUNDED:
|
||||||
$class = 'default';
|
$class = 'default';
|
||||||
break;
|
break;
|
||||||
@ -793,13 +863,19 @@ class PaymentService extends BaseService
|
|||||||
|
|
||||||
$amount = min($amount, $payment->amount - $payment->refunded);
|
$amount = min($amount, $payment->amount - $payment->refunded);
|
||||||
|
|
||||||
if (!$amount) {
|
$accountGateway = $payment->account_gateway;
|
||||||
|
|
||||||
|
if (!$accountGateway) {
|
||||||
|
$accountGateway = AccountGateway::withTrashed()->find($payment->account_gateway_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$amount || !$accountGateway) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($payment->payment_type_id != PAYMENT_TYPE_CREDIT) {
|
if ($payment->payment_type_id != PAYMENT_TYPE_CREDIT) {
|
||||||
$accountGateway = $this->createGateway($payment->account_gateway);
|
$gateway = $this->createGateway($accountGateway);
|
||||||
$refund = $accountGateway->refund(array(
|
$refund = $gateway->refund(array(
|
||||||
'transactionReference' => $payment->transaction_reference,
|
'transactionReference' => $payment->transaction_reference,
|
||||||
'amount' => $amount,
|
'amount' => $amount,
|
||||||
));
|
));
|
||||||
@ -808,11 +884,49 @@ class PaymentService extends BaseService
|
|||||||
if ($response->isSuccessful()) {
|
if ($response->isSuccessful()) {
|
||||||
$payment->recordRefund($amount);
|
$payment->recordRefund($amount);
|
||||||
} else {
|
} else {
|
||||||
|
$data = $response->getData();
|
||||||
|
|
||||||
|
if ($data instanceof \Braintree\Result\Error) {
|
||||||
|
$error = $data->errors->deepAll()[0];
|
||||||
|
if ($error && $error->code == 91506) {
|
||||||
|
if ($amount == $payment->amount) {
|
||||||
|
// This is an unsettled transaction; try to void it
|
||||||
|
$void = $gateway->void(array(
|
||||||
|
'transactionReference' => $payment->transaction_reference,
|
||||||
|
));
|
||||||
|
$response = $void->send();
|
||||||
|
|
||||||
|
if ($response->isSuccessful()) {
|
||||||
|
$payment->markVoided();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->error('Unknown', 'Partial refund not allowed for unsettled transactions.', $accountGateway);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$response->isSuccessful()) {
|
||||||
$this->error('Unknown', $response->getMessage(), $accountGateway);
|
$this->error('Unknown', $response->getMessage(), $accountGateway);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$payment->recordRefund($amount);
|
$payment->recordRefund($amount);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function error($type, $error, $accountGateway = false, $exception = false)
|
||||||
|
{
|
||||||
|
$message = '';
|
||||||
|
if ($accountGateway && $accountGateway->gateway) {
|
||||||
|
$message = $accountGateway->gateway->name . ': ';
|
||||||
|
}
|
||||||
|
$message .= $error ?: trans('texts.payment_error');
|
||||||
|
|
||||||
|
Session::flash('error', $message);
|
||||||
|
Utils::logError("Payment Error [{$type}]: " . ($exception ? Utils::getErrorString($exception) : $message), 'PHP', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function makeStripeCall($accountGateway, $method, $url, $body = null) {
|
public function makeStripeCall($accountGateway, $method, $url, $body = null) {
|
||||||
|
@ -25,7 +25,7 @@ class PaymentsChanges extends Migration
|
|||||||
Schema::table('payments', function($table)
|
Schema::table('payments', function($table)
|
||||||
{
|
{
|
||||||
$table->decimal('refunded', 13, 2);
|
$table->decimal('refunded', 13, 2);
|
||||||
$table->unsignedInteger('payment_status_id')->default(3);
|
$table->unsignedInteger('payment_status_id')->default(PAYMENT_STATUS_COMPLETED);
|
||||||
$table->foreign('payment_status_id')->references('id')->on('payment_statuses');
|
$table->foreign('payment_status_id')->references('id')->on('payment_statuses');
|
||||||
|
|
||||||
$table->unsignedInteger('routing_number')->nullable();
|
$table->unsignedInteger('routing_number')->nullable();
|
||||||
|
@ -17,10 +17,11 @@ class PaymentStatusSeeder extends Seeder
|
|||||||
{
|
{
|
||||||
$statuses = [
|
$statuses = [
|
||||||
['id' => '1', 'name' => 'Pending'],
|
['id' => '1', 'name' => 'Pending'],
|
||||||
['id' => '2', 'name' => 'Failed'],
|
['id' => '2', 'name' => 'Voided'],
|
||||||
['id' => '3', 'name' => 'Completed'],
|
['id' => '3', 'name' => 'Failed'],
|
||||||
['id' => '4', 'name' => 'Partially Refunded'],
|
['id' => '4', 'name' => 'Completed'],
|
||||||
['id' => '5', 'name' => 'Refunded'],
|
['id' => '5', 'name' => 'Partially Refunded'],
|
||||||
|
['id' => '6', 'name' => 'Refunded'],
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($statuses as $status) {
|
foreach ($statuses as $status) {
|
||||||
|
@ -1188,8 +1188,10 @@ $LANG = array(
|
|||||||
'status_partially_refunded' => 'Partially Refunded',
|
'status_partially_refunded' => 'Partially Refunded',
|
||||||
'status_partially_refunded_amount' => ':amount Refunded',
|
'status_partially_refunded_amount' => ':amount Refunded',
|
||||||
'status_refunded' => 'Refunded',
|
'status_refunded' => 'Refunded',
|
||||||
|
'status_voided' => 'Cancelled',
|
||||||
'refunded_payment' => 'Refunded Payment',
|
'refunded_payment' => 'Refunded Payment',
|
||||||
'activity_39' => ':user refunded :adjustment of a :payment_amount payment (:payment)',
|
'activity_39' => ':user cancelled a :payment_amount payment (:payment)',
|
||||||
|
'activity_40' => ':user refunded :adjustment of a :payment_amount payment (:payment)',
|
||||||
'card_expiration' => 'Exp: :expires',
|
'card_expiration' => 'Exp: :expires',
|
||||||
|
|
||||||
'card_creditcardother' => 'Unknown',
|
'card_creditcardother' => 'Unknown',
|
||||||
@ -1252,7 +1254,7 @@ $LANG = array(
|
|||||||
'use_for_auto_bill' => 'Use For Autobill',
|
'use_for_auto_bill' => 'Use For Autobill',
|
||||||
'used_for_auto_bill' => 'Autobill Payment Method',
|
'used_for_auto_bill' => 'Autobill Payment Method',
|
||||||
'payment_method_set_as_default' => 'Set Autobill payment method.',
|
'payment_method_set_as_default' => 'Set Autobill payment method.',
|
||||||
'activity_40' => ':payment_amount payment (:payment) failed',
|
'activity_41' => ':payment_amount payment (:payment) failed',
|
||||||
'webhook_url' => 'Webhook URL',
|
'webhook_url' => 'Webhook URL',
|
||||||
'stripe_webhook_help' => 'You must :link for ACH payment status to be updated.',
|
'stripe_webhook_help' => 'You must :link for ACH payment status to be updated.',
|
||||||
'stripe_webhook_help_link_text' => 'add this URL as an endpoint at Stripe',
|
'stripe_webhook_help_link_text' => 'add this URL as an endpoint at Stripe',
|
||||||
@ -1274,7 +1276,7 @@ $LANG = array(
|
|||||||
'disabled_by_client' => 'Disabled by client',
|
'disabled_by_client' => 'Disabled by client',
|
||||||
'manage_auto_bill' => 'Manage Auto-bill',
|
'manage_auto_bill' => 'Manage Auto-bill',
|
||||||
'enabled' => 'Enabled',
|
'enabled' => 'Enabled',
|
||||||
'disabled' => 'Disabled',
|
''
|
||||||
);
|
);
|
||||||
|
|
||||||
return $LANG;
|
return $LANG;
|
||||||
|
@ -161,7 +161,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if (!empty($paymentMethods))
|
@if (!empty($account->getTokenGatewayId()))
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<h3>{{ trans('texts.payment_methods') }}</h3>
|
<h3>{{ trans('texts.payment_methods') }}</h3>
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: function(e) {
|
onError: function(e) {
|
||||||
|
var $form = $('.payment-form');
|
||||||
|
$form.find('button').prop('disabled', false);
|
||||||
// Show the errors on the form
|
// Show the errors on the form
|
||||||
if (e.details && e.details.invalidFieldKeys.length) {
|
if (e.details && e.details.invalidFieldKeys.length) {
|
||||||
var invalidField = e.details.invalidFieldKeys[0];
|
var invalidField = e.details.invalidFieldKeys[0];
|
||||||
@ -54,6 +56,10 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
$('.payment-form').submit(function(event) {
|
$('.payment-form').submit(function(event) {
|
||||||
|
var $form = $(this);
|
||||||
|
|
||||||
|
// Disable the submit button to prevent repeated clicks
|
||||||
|
$form.find('button').prop('disabled', true);
|
||||||
$('#js-error-message').hide();
|
$('#js-error-message').hide();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
display:inline-block;
|
display:inline-block;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@if(!empty($paymentMethods))
|
||||||
@foreach ($paymentMethods as $paymentMethod)
|
@foreach ($paymentMethods as $paymentMethod)
|
||||||
<div class="payment_method">
|
<div class="payment_method">
|
||||||
<span class="payment_method_img_container">
|
<span class="payment_method_img_container">
|
||||||
@ -41,6 +42,7 @@
|
|||||||
<a href="javasript::void" class="payment_method_remove" onclick="removePaymentMethod('{{$paymentMethod['id']}}')">×</a>
|
<a href="javasript::void" class="payment_method_remove" onclick="removePaymentMethod('{{$paymentMethod['id']}}')">×</a>
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
@endif
|
||||||
|
|
||||||
<center>
|
<center>
|
||||||
{!! Button::success(strtoupper(trans('texts.add_credit_card')))
|
{!! Button::success(strtoupper(trans('texts.add_credit_card')))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user