mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-03 19:47:34 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			314 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			314 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
/**
 | 
						|
 * Invoice Ninja (https://invoiceninja.com).
 | 
						|
 *
 | 
						|
 * @link https://github.com/invoiceninja/invoiceninja source repository
 | 
						|
 *
 | 
						|
 * @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
 | 
						|
 *
 | 
						|
 * @license https://opensource.org/licenses/AAL
 | 
						|
 */
 | 
						|
 | 
						|
namespace App\PaymentDrivers;
 | 
						|
 | 
						|
use App\Factory\PaymentFactory;
 | 
						|
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
 | 
						|
use App\Models\Client;
 | 
						|
use App\Models\ClientContact;
 | 
						|
use App\Models\CompanyGateway;
 | 
						|
use App\Models\GatewayType;
 | 
						|
use App\Models\Invoice;
 | 
						|
use App\Models\Payment;
 | 
						|
use App\Models\PaymentHash;
 | 
						|
use App\Utils\Traits\MakesHash;
 | 
						|
use App\Utils\Traits\SystemLogTrait;
 | 
						|
use Illuminate\Support\Carbon;
 | 
						|
use Omnipay\Omnipay;
 | 
						|
 | 
						|
/**
 | 
						|
 * Class BasePaymentDriver.
 | 
						|
        'amount' => $invoice->getRequestedAmount(),
 | 
						|
        'currency' => $invoice->getCurrencyCode(),
 | 
						|
        'returnUrl' => $completeUrl,
 | 
						|
        'cancelUrl' => $this->invitation->getLink(),
 | 
						|
        'description' => trans('texts.' . $invoice->getEntityType()) . " {$invoice->number}",
 | 
						|
        'transactionId' => $invoice->number,
 | 
						|
        'transactionType' => 'Purchase',
 | 
						|
        'clientIp' => Request::getClientIp(),
 | 
						|
    ];
 | 
						|
 */
 | 
						|
class BasePaymentDriver
 | 
						|
{
 | 
						|
    use SystemLogTrait;
 | 
						|
    use MakesHash;
 | 
						|
 | 
						|
    /* The company gateway instance*/
 | 
						|
    public $company_gateway;
 | 
						|
 | 
						|
    /* The Omnipay payment driver instance*/
 | 
						|
    protected $gateway;
 | 
						|
 | 
						|
    /* The Invitation */
 | 
						|
    protected $invitation;
 | 
						|
 | 
						|
    /* Gateway capabilities */
 | 
						|
    protected $refundable = false;
 | 
						|
 | 
						|
    /* Token billing */
 | 
						|
    protected $token_billing = false;
 | 
						|
 | 
						|
    /* Authorise payment methods */
 | 
						|
    protected $can_authorise_credit_card = false;
 | 
						|
 | 
						|
    public function __construct(CompanyGateway $company_gateway, Client $client, $invitation = false)
 | 
						|
    {
 | 
						|
        $this->company_gateway = $company_gateway;
 | 
						|
 | 
						|
        $this->invitation = $invitation;
 | 
						|
 | 
						|
        $this->client = $client;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the Omnipay driver.
 | 
						|
     * @return stdClass Omnipay initialized object
 | 
						|
     */
 | 
						|
    protected function gateway()
 | 
						|
    {
 | 
						|
        $this->gateway = Omnipay::create($this->company_gateway->gateway->provider);
 | 
						|
        $this->gateway->initialize((array) $this->company_gateway->getConfig());
 | 
						|
 | 
						|
        return $this;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return the configuration fields for the
 | 
						|
     * Gatway.
 | 
						|
     * @return array The configuration fields
 | 
						|
     */
 | 
						|
    public function getFields()
 | 
						|
    {
 | 
						|
        return $this->gateway->getDefaultParameters();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the default gateway type.
 | 
						|
     */
 | 
						|
    public function gatewayTypes()
 | 
						|
    {
 | 
						|
        return [
 | 
						|
            GatewayType::CREDIT_CARD,
 | 
						|
        ];
 | 
						|
    }
 | 
						|
 | 
						|
    public function getCompanyGatewayId(): int
 | 
						|
    {
 | 
						|
        return $this->company_gateway->id;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns whether refunds are possible with the gateway.
 | 
						|
     * @return bool TRUE|FALSE
 | 
						|
     */
 | 
						|
    public function getRefundable(): bool
 | 
						|
    {
 | 
						|
        return $this->refundable;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns whether token billing is possible with the gateway.
 | 
						|
     * @return bool TRUE|FALSE
 | 
						|
     */
 | 
						|
    public function getTokenBilling(): bool
 | 
						|
    {
 | 
						|
        return $this->token_billing;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns whether gateway can
 | 
						|
     * authorise and credit card.
 | 
						|
     * @return bool [type] [description]
 | 
						|
     */
 | 
						|
    public function canAuthoriseCreditCard(): bool
 | 
						|
    {
 | 
						|
        return $this->can_authorise_credit_card;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Refunds a given payment.
 | 
						|
     * @param $payment
 | 
						|
     * @param int $amount
 | 
						|
     * @return false
 | 
						|
     */
 | 
						|
    public function refundPayment($payment, $amount = 0)
 | 
						|
    {
 | 
						|
        if ($amount) {
 | 
						|
            $amount = min($amount, $payment->getCompletedAmount());
 | 
						|
        } else {
 | 
						|
            $amount = $payment->getCompletedAmount();
 | 
						|
        }
 | 
						|
 | 
						|
        if ($payment->is_deleted || ! $amount) {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        if ($payment->type_id == Payment::TYPE_CREDIT_CARD) {
 | 
						|
            return $payment->recordRefund($amount);
 | 
						|
        }
 | 
						|
 | 
						|
        $details = $this->refundDetails($payment, $amount);
 | 
						|
        $response = $this->gateway()->refund($details)->send();
 | 
						|
 | 
						|
        if ($response->isSuccessful()) {
 | 
						|
            return $payment->recordRefund($amount);
 | 
						|
        } elseif ($this->attemptVoidPayment($response, $payment, $amount)) {
 | 
						|
            $details = ['transactionReference' => $payment->transaction_reference];
 | 
						|
            $response = $this->gateway->void($details)->send();
 | 
						|
            if ($response->isSuccessful()) {
 | 
						|
                return $payment->markVoided();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    protected function attemptVoidPayment($response, $payment, $amount)
 | 
						|
    {
 | 
						|
        // Partial refund not allowed for unsettled transactions
 | 
						|
        return $amount == $payment->amount;
 | 
						|
    }
 | 
						|
 | 
						|
    public function authorizeCreditCardView(array $data)
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    public function authorizeCreditCardResponse($request)
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    public function processPaymentView(array $data)
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    public function processPaymentResponse($request)
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return the contact if possible.
 | 
						|
     *
 | 
						|
     * @return ClientContact The ClientContact object
 | 
						|
     */
 | 
						|
    public function getContact()
 | 
						|
    {
 | 
						|
        if ($this->invitation) {
 | 
						|
            return ClientContact::find($this->invitation->client_contact_id);
 | 
						|
        } elseif (auth()->guard('contact')->user()) {
 | 
						|
            return auth()->user();
 | 
						|
        } else {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /************************************* Omnipay ******************************************
 | 
						|
     * authorize($options) - authorize an amount on the customer's card
 | 
						|
     * completeAuthorize($options) - handle return from off-site gateways after authorization
 | 
						|
     * capture($options) - capture an amount you have previously authorized
 | 
						|
     * purchase($options) - authorize and immediately capture an amount on the customer's card
 | 
						|
     * completePurchase($options) - handle return from off-site gateways after purchase
 | 
						|
     * refund($options) - refund an already processed transaction
 | 
						|
     * void($options) - generally can only be called up to 24 hours after submitting a transaction
 | 
						|
     * acceptNotification() - convert an incoming request from an off-site gateway to a generic notification object for further processing
 | 
						|
     * @param $input
 | 
						|
     * @return array
 | 
						|
     */
 | 
						|
 | 
						|
    protected function paymentDetails($input): array
 | 
						|
    {
 | 
						|
        $data = [
 | 
						|
            'currency' => $this->client->getCurrencyCode(),
 | 
						|
            'transactionType' => 'Purchase',
 | 
						|
            'clientIp' => request()->getClientIp(),
 | 
						|
        ];
 | 
						|
 | 
						|
        return $data;
 | 
						|
    }
 | 
						|
 | 
						|
    public function purchase($data, $items)
 | 
						|
    {
 | 
						|
        $this->gateway();
 | 
						|
 | 
						|
        $response = $this->gateway
 | 
						|
                        ->purchase($data)
 | 
						|
                        ->setItems($items)
 | 
						|
                        ->send();
 | 
						|
 | 
						|
        return $response;
 | 
						|
    }
 | 
						|
 | 
						|
    public function completePurchase($data)
 | 
						|
    {
 | 
						|
        $this->gateway();
 | 
						|
 | 
						|
        return $this->gateway
 | 
						|
            ->completePurchase($data)
 | 
						|
            ->send();
 | 
						|
    }
 | 
						|
 | 
						|
    public function createPayment($data, $status = Payment::STATUS_COMPLETED): Payment
 | 
						|
    {
 | 
						|
        $payment = PaymentFactory::create($this->client->company->id, $this->client->user->id);
 | 
						|
        $payment->client_id = $this->client->id;
 | 
						|
        $payment->company_gateway_id = $this->company_gateway->id;
 | 
						|
        $payment->status_id = $status;
 | 
						|
        $payment->currency_id = $this->client->getSetting('currency_id');
 | 
						|
        $payment->date = Carbon::now();
 | 
						|
 | 
						|
        return $payment->service()->applyNumber()->save();
 | 
						|
    }
 | 
						|
 | 
						|
    public function attachInvoices(Payment $payment, PaymentHash $payment_hash): Payment
 | 
						|
    {
 | 
						|
        $paid_invoices = $payment_hash->invoices();
 | 
						|
        $invoices = Invoice::whereIn('id', $this->transformKeys(array_column($paid_invoices, 'invoice_id')))->get();
 | 
						|
        $payment->invoices()->sync($invoices);
 | 
						|
        $payment->save();
 | 
						|
 | 
						|
        return $payment;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * When a successful payment is made, we need to append the gateway fee
 | 
						|
     * to an invoice.
 | 
						|
     *
 | 
						|
     * @param  PaymentResponseRequest $request The incoming payment request
 | 
						|
     * @return void                            Success/Failure
 | 
						|
     */
 | 
						|
    public function confirmGatewayFee(PaymentResponseRequest $request) :void
 | 
						|
    {
 | 
						|
        /*Payment meta data*/
 | 
						|
        $payment_hash = $request->getPaymentHash();
 | 
						|
 | 
						|
        /*Payment invoices*/
 | 
						|
        $payment_invoices = $payment_hash->invoices();
 | 
						|
 | 
						|
        // /*Fee charged at gateway*/
 | 
						|
        $fee_total = $payment_hash->fee_total;
 | 
						|
 | 
						|
        // Sum of invoice amounts
 | 
						|
        // $invoice_totals = array_sum(array_column($payment_invoices,'amount'));
 | 
						|
 | 
						|
        /*Hydrate invoices*/
 | 
						|
        $invoices = Invoice::whereIn('id', $this->transformKeys(array_column($payment_invoices, 'invoice_id')))->get();
 | 
						|
 | 
						|
        $invoices->each(function ($invoice) use ($fee_total) {
 | 
						|
            if (collect($invoice->line_items)->contains('type_id', '3')) {
 | 
						|
                $invoice->service()->toggleFeesPaid()->save();
 | 
						|
                $invoice->client->service()->updateBalance($fee_total)->save();
 | 
						|
                $invoice->ledger()->updateInvoiceBalance($fee_total, "Gateway fee adjustment for Invoice {$invoice->number}");
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
}
 |