mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 05:02:36 +01:00
Scaffolding Paytrace
This commit is contained in:
parent
11ae85732b
commit
c0287085b5
@ -91,6 +91,7 @@ class CreateAccount extends Command
|
||||
$account = Account::factory()->create();
|
||||
$company = Company::factory()->create([
|
||||
'account_id' => $account->id,
|
||||
'domain' => config('ninja.app_url'),
|
||||
]);
|
||||
|
||||
$account->default_company_id = $company->id;
|
||||
|
@ -69,16 +69,20 @@ class CompanyGateway extends BaseModel
|
||||
// const TYPE_CUSTOM = 306;
|
||||
// const TYPE_BRAINTREE = 307;
|
||||
// const TYPE_WEPAY = 309;
|
||||
// const TYPE_PAYFAST = 310;
|
||||
// const TYPE_PAYTRACE = 311;
|
||||
|
||||
public $gateway_consts = [
|
||||
'38f2c48af60c7dd69e04248cbb24c36e' => 300,
|
||||
'd14dd26a37cecc30fdd65700bfb55b23' => 301,
|
||||
'd14dd26a47cecc30fdd65700bfb67b34' => 301,
|
||||
'3758e7f7c6f4cecf0f4f348b9a00f456' => 304,
|
||||
'3b6621f970ab18887c4f6dca78d3f8bb' => 305,
|
||||
'54faab2ab6e3223dbe848b1686490baa' => 306,
|
||||
'd14dd26a47cecc30fdd65700bfb67b34' => 301,
|
||||
'8fdeed552015b3c7b44ed6c8ebd9e992' => 309,
|
||||
'f7ec488676d310683fb51802d076d713' => 307,
|
||||
'8fdeed552015b3c7b44ed6c8ebd9e992' => 309,
|
||||
'd6814fc83f45d2935e7777071e629ef9' => 310,
|
||||
'bbd736b3254b0aabed6ad7fda1298c88' => 311,
|
||||
];
|
||||
|
||||
protected $touches = [];
|
||||
|
@ -68,7 +68,7 @@ class SystemLog extends Model
|
||||
const TYPE_BRAINTREE = 307;
|
||||
const TYPE_WEPAY = 309;
|
||||
const TYPE_PAYFAST = 310;
|
||||
|
||||
const TYPE_PAYTRACE = 311;
|
||||
|
||||
const TYPE_QUOTA_EXCEEDED = 400;
|
||||
const TYPE_UPSTREAM_FAILURE = 401;
|
||||
|
121
app/PaymentDrivers/PayTrace/CreditCard.php
Normal file
121
app/PaymentDrivers/PayTrace/CreditCard.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?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://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\PayTrace;
|
||||
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\PayFastPaymentDriver;
|
||||
use App\PaymentDrivers\PaytracePaymentDriver;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CreditCard
|
||||
{
|
||||
|
||||
public $paytrace_driver;
|
||||
|
||||
public function __construct(PaytracePaymentDriver $paytrace_driver)
|
||||
{
|
||||
$this->paytrace_driver = $paytrace_driver;
|
||||
}
|
||||
|
||||
public function authorizeView($data)
|
||||
{
|
||||
|
||||
$data['client_key'] = $this->paytrace_driver->getAuthToken();
|
||||
|
||||
return render('gateways.paytrace.authorize', $data);
|
||||
}
|
||||
|
||||
|
||||
public function authorizeResponse($request)
|
||||
{
|
||||
$data = $request->all();
|
||||
|
||||
return response()->json([], 200);
|
||||
|
||||
}
|
||||
|
||||
public function paymentView($data)
|
||||
{
|
||||
|
||||
|
||||
return render('gateways.paytrace.pay', $data);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function paymentResponse(Request $request)
|
||||
{
|
||||
$response_array = $request->all();
|
||||
|
||||
|
||||
|
||||
// if($response_array['payment_status'] == 'COMPLETE') {
|
||||
|
||||
// $this->payfast->logSuccessfulGatewayResponse(['response' => $response_array, 'data' => $this->paytrace_driver->payment_hash], SystemLog::TYPE_PAYFAST);
|
||||
|
||||
// return $this->processSuccessfulPayment($response_array);
|
||||
// }
|
||||
// else {
|
||||
// $this->processUnsuccessfulPayment($response_array);
|
||||
// }
|
||||
}
|
||||
|
||||
private function processSuccessfulPayment($response_array)
|
||||
{
|
||||
|
||||
|
||||
|
||||
// $payment = $this->paytrace_driver->createPayment($payment_record, Payment::STATUS_COMPLETED);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private function processUnsuccessfulPayment($server_response)
|
||||
{
|
||||
// PaymentFailureMailer::dispatch($this->paytrace_driver->client, $server_response->cancellation_reason, $this->paytrace_driver->client->company, $server_response->amount);
|
||||
|
||||
// PaymentFailureMailer::dispatch(
|
||||
// $this->paytrace_driver->client,
|
||||
// $server_response,
|
||||
// $this->paytrace_driver->client->company,
|
||||
// $server_response['amount_gross']
|
||||
// );
|
||||
|
||||
// $message = [
|
||||
// 'server_response' => $server_response,
|
||||
// 'data' => $this->paytrace_driver->payment_hash->data,
|
||||
// ];
|
||||
|
||||
// SystemLogger::dispatch(
|
||||
// $message,
|
||||
// SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
// SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
// SystemLog::TYPE_PAYFAST,
|
||||
// $this->payfast->client,
|
||||
// $this->payfast->client->company,
|
||||
// );
|
||||
|
||||
// throw new PaymentFailed('Failed to process the payment.', 500);
|
||||
}
|
||||
|
||||
}
|
123
app/PaymentDrivers/PaytracePaymentDriver.php
Normal file
123
app/PaymentDrivers/PaytracePaymentDriver.php
Normal file
@ -0,0 +1,123 @@
|
||||
<?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://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\PayTrace\CreditCard;
|
||||
use App\Utils\CurlUtils;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class PaytracePaymentDriver extends BaseDriver
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public $refundable = true;
|
||||
|
||||
public $token_billing = true;
|
||||
|
||||
public $can_authorise_credit_card = true;
|
||||
|
||||
public $gateway;
|
||||
|
||||
public $payment_method;
|
||||
|
||||
public static $methods = [
|
||||
GatewayType::CREDIT_CARD => CreditCard::class, //maps GatewayType => Implementation class
|
||||
];
|
||||
|
||||
const SYSTEM_LOG_TYPE = SystemLog::TYPE_PAYTRACE; //define a constant for your gateway ie TYPE_YOUR_CUSTOM_GATEWAY - set the const in the SystemLog model
|
||||
|
||||
public function init()
|
||||
{
|
||||
return $this; /* This is where you boot the gateway with your auth credentials*/
|
||||
}
|
||||
|
||||
/* Returns an array of gateway types for the payment gateway */
|
||||
public function gatewayTypes(): array
|
||||
{
|
||||
$types = [];
|
||||
|
||||
$types[] = GatewayType::CREDIT_CARD;
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/* Sets the payment method initialized */
|
||||
public function setPaymentMethod($payment_method_id)
|
||||
{
|
||||
$class = self::$methods[$payment_method_id];
|
||||
$this->payment_method = new $class($this);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function authorizeView(array $data)
|
||||
{
|
||||
return $this->payment_method->authorizeView($data); //this is your custom implementation from here
|
||||
}
|
||||
|
||||
public function authorizeResponse($request)
|
||||
{
|
||||
return $this->payment_method->authorizeResponse($request); //this is your custom implementation from here
|
||||
}
|
||||
|
||||
public function processPaymentView(array $data)
|
||||
{
|
||||
return $this->payment_method->paymentView($data); //this is your custom implementation from here
|
||||
}
|
||||
|
||||
public function processPaymentResponse($request)
|
||||
{
|
||||
return $this->payment_method->paymentResponse($request); //this is your custom implementation from here
|
||||
}
|
||||
|
||||
public function refund(Payment $payment, $amount, $return_client_response = false)
|
||||
{
|
||||
return $this->payment_method->yourRefundImplementationHere(); //this is your custom implementation from here
|
||||
}
|
||||
|
||||
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
|
||||
{
|
||||
return $this->payment_method->yourTokenBillingImplmentation(); //this is your custom implementation from here
|
||||
}
|
||||
|
||||
public function processWebhookRequest(PaymentWebhookRequest $request, Payment $payment = null)
|
||||
{
|
||||
}
|
||||
|
||||
/*Helpers*/
|
||||
|
||||
public function getAuthToken()
|
||||
{
|
||||
|
||||
$url = 'https://api.paytrace.com/oauth/token';
|
||||
$data = [
|
||||
'grant_type' => 'password',
|
||||
'username' => $this->company_gateway->getConfigField('username'),
|
||||
'password' => $this->company_gateway->getConfigField('password')
|
||||
];
|
||||
|
||||
$response = CurlUtils::post($url, $data, $headers = false);
|
||||
|
||||
if($response)
|
||||
{
|
||||
$auth_data = json_decode($response);
|
||||
|
||||
return $auth_data->access_token;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Gateway;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ActivatePaytracePaymentDriver extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Gateway::query()->where('id', 46)->update(['visible' => true, 'provider' => 'Paytrace']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -70,7 +70,7 @@ class PaymentLibrariesSeeder extends Seeder
|
||||
['id' => 43, 'name' => 'Fasapay', 'provider' => 'Fasapay', 'key' => '1b2cef0e8c800204a29f33953aaf3360', 'fields' => ''],
|
||||
['id' => 44, 'name' => 'Komoju', 'provider' => 'Komoju', 'key' => '7ea2d40ecb1eb69ef8c3d03e5019028a', 'fields' => '{"apiKey":"","accountId":"","paymentMethod":"credit_card","testMode":false,"locale":"en"}'],
|
||||
['id' => 45, 'name' => 'Paysafecard', 'provider' => 'Paysafecard', 'key' => '70ab90cd6c5c1ab13208b3cef51c0894', 'fields' => '{"username":"","password":"","testMode":false}'],
|
||||
['id' => 46, 'name' => 'Paytrace', 'provider' => 'Paytrace_CreditCard', 'key' => 'bbd736b3254b0aabed6ad7fda1298c88', 'fields' => '{"username":"","password":"","testMode":false,"endpoint":"https:\/\/paytrace.com\/api\/default.pay"}'],
|
||||
['id' => 46, 'name' => 'Paytrace', 'provider' => 'Paytrace', 'key' => 'bbd736b3254b0aabed6ad7fda1298c88', 'fields' => '{"username":"","password":"","testMode":false,"endpoint":"https:\/\/paytrace.com\/api\/default.pay"}'],
|
||||
['id' => 47, 'name' => 'Secure Trading', 'provider' => 'SecureTrading', 'key' => '231cb401487b9f15babe04b1ac4f7a27', 'fields' => '{"siteReference":"","username":"","password":"","applyThreeDSecure":false,"accountType":"ECOM"}'],
|
||||
['id' => 48, 'name' => 'SecPay', 'provider' => 'SecPay', 'key' => 'bad8699d581d9fa040e59c0bb721a76c', 'fields' => '{"mid":"","vpnPswd":"","remotePswd":"","usageType":"","confirmEmail":"","testStatus":"true","mailCustomer":"true","additionalOptions":""}'],
|
||||
['id' => 49, 'name' => 'WePay', 'provider' => 'WePay', 'is_offsite' => false, 'sort_order' => 3, 'key' => '8fdeed552015b3c7b44ed6c8ebd9e992', 'fields' => '{"accountId":"","accessToken":"","type":"goods","testMode":false,"feePayer":"payee"}'],
|
||||
@ -96,7 +96,7 @@ class PaymentLibrariesSeeder extends Seeder
|
||||
|
||||
Gateway::query()->update(['visible' => 0]);
|
||||
|
||||
Gateway::whereIn('id', [1,15,20,39,55,50])->update(['visible' => 1]);
|
||||
Gateway::whereIn('id', [1,15,20,39,46,55,50])->update(['visible' => 1]);
|
||||
|
||||
if (Ninja::isHosted()) {
|
||||
Gateway::whereIn('id', [20])->update(['visible' => 0]);
|
||||
|
@ -0,0 +1,162 @@
|
||||
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.credit_card'), 'card_title' => ctrans('texts.credit_card')])
|
||||
|
||||
@section('gateway_head')
|
||||
@endsection
|
||||
|
||||
@section('gateway_content')
|
||||
|
||||
@if(!Request::isSecure())
|
||||
<p class="alert alert-failure">{{ ctrans('texts.https_required') }}</p>
|
||||
@endif
|
||||
|
||||
<div class="alert alert-failure mb-4" hidden id="errors"></div>
|
||||
|
||||
<div class="w-screen items-center">
|
||||
|
||||
<div id='pt_hpf_form'><!--iframe sensitive data payment fields inserted here--></div>
|
||||
|
||||
</div>
|
||||
|
||||
<form action="{{ route('client.payment_methods.store', ['method' => App\Models\GatewayType::CREDIT_CARD]) }}" id="ProtectForm">
|
||||
|
||||
<div class="bg-white px-4 py-5 flex justify-end">
|
||||
<button
|
||||
type="submit"
|
||||
id="{{ $id ?? 'pay-now' }}"
|
||||
class="button button-primary bg-primary {{ $class ?? '' }}">
|
||||
<span>{{ ctrans('texts.add_payment_method') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
@endsection
|
||||
|
||||
@section('gateway_footer')
|
||||
|
||||
<script src='https://protect.paytrace.com/js/protect.min.js'></script>
|
||||
|
||||
<script>
|
||||
|
||||
// Minimal Protect.js setup call
|
||||
PTPayment.setup({
|
||||
styles:
|
||||
{
|
||||
'code': {
|
||||
'font_color':'#5D99CA',
|
||||
'border_color':'#EF9F6D',
|
||||
'border_style':'dotted',
|
||||
'font_size':'13pt',
|
||||
'input_border_radius':'10px',
|
||||
'input_border_width':'2px',
|
||||
'input_font':'serif, cursive, fantasy',
|
||||
'input_font_weight':'700',
|
||||
'input_margin':'5px 0px 5px 20px',
|
||||
'input_padding':'0px 5px 0px 5px',
|
||||
'label_color':'#5D99CA',
|
||||
'label_size':'16px',
|
||||
'label_width':'150px',
|
||||
'label_font':'sans-serif, arial, serif',
|
||||
'label_font_weight':'bold',
|
||||
'label_margin':'5px 0px 0px 20px',
|
||||
'label_padding':'2px 5px 2px 5px',
|
||||
'label_border_style':'dotted',
|
||||
'label_border_color':'#EF9F6D',
|
||||
'label_border_radius':'10px',
|
||||
'label_border_width':'2px',
|
||||
'background_color':'white',
|
||||
'height':'25px',
|
||||
'width':'110px',
|
||||
'padding_bottom':'2px'
|
||||
},
|
||||
'cc': {
|
||||
'font_color':'#5D99CA',
|
||||
'border_color':'#EF9F6D',
|
||||
'border_style':'solid',
|
||||
'font_size':'13pt',
|
||||
'input_border_radius':'20px',
|
||||
'input_border_width':'2px',
|
||||
'input_font':'Times New Roman, arial, fantasy',
|
||||
'input_font_weight':'400',
|
||||
'input_margin':'5px 0px 5px 0px',
|
||||
'input_padding':'0px 5px 0px 5px',
|
||||
'label_color':'#5D99CA',
|
||||
'label_size':'16px',
|
||||
'label_width':'150px',
|
||||
'label_font':'Times New Roman, sans-serif, serif',
|
||||
'label_font_weight':'light',
|
||||
'label_margin':'5px 0px 0px 0px',
|
||||
'label_padding':'0px 5px 0px 5px',
|
||||
'label_border_style':'solid',
|
||||
'label_border_color':'#EF9F6D',
|
||||
'label_border_radius':'20px',
|
||||
'label_border_width':'2px',
|
||||
'background_color':'white',
|
||||
'height':'25px',
|
||||
'width':'320px',
|
||||
'padding_bottom':'0px'
|
||||
},
|
||||
'exp': {
|
||||
'font_color':'#5D99CA',
|
||||
'border_color':'#EF9F6D',
|
||||
'border_style':'dashed',
|
||||
'font_size':'12pt',
|
||||
'input_border_radius':'0px',
|
||||
'input_border_width':'2px',
|
||||
'input_font':'arial, cursive, fantasy',
|
||||
'input_font_weight':'400',
|
||||
'input_margin':'5px 0px 5px 0px',
|
||||
'input_padding':'0px 5px 0px 5px',
|
||||
'label_color':'#5D99CA',
|
||||
'label_size':'16px',
|
||||
'label_width':'150px',
|
||||
'label_font':'arial, fantasy, serif',
|
||||
'label_font_weight':'normal',
|
||||
'label_margin':'5px 0px 0px 0px',
|
||||
'label_padding':'2px 5px 2px 5px',
|
||||
'label_border_style':'dashed',
|
||||
'label_border_color':'#EF9F6D',
|
||||
'label_border_radius':'0px',
|
||||
'label_border_width':'2px',
|
||||
'background_color':'white',
|
||||
'height':'25px',
|
||||
'width':'85px',
|
||||
'padding_bottom':'2px',
|
||||
'type':'dropdown'
|
||||
},
|
||||
'body': {
|
||||
'background_color':'white'
|
||||
}
|
||||
},
|
||||
authorization: { clientKey: "{!! $client_key !!}" }
|
||||
}).then(function(instance){
|
||||
//use instance object to process and tokenize sensitive data payment fields.
|
||||
PTPayment.theme('above the line');
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
// this can be any event we chose. We will use the submit event and stop any default event handling and prevent event handling bubbling.
|
||||
document.getElementById("ProtectForm").addEventListener("submit",function(e){
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// To trigger the validation of sensitive data payment fields within the iframe before calling the tokenization process:
|
||||
PTPayment.validate(function(validationErrors) {
|
||||
if (validationErrors.length >= 1) {
|
||||
if (validationErrors[0]['responseCode'] == '35') {
|
||||
// Handle validation Errors here
|
||||
// This is an example of using dynamic styling to show the Credit card number entered is invalid
|
||||
PTPayment.style({'cc': {'border_color': 'red'}});
|
||||
}
|
||||
} else {
|
||||
// no error so tokenize
|
||||
instance.process()
|
||||
.then( (r) => submitPayment(r) )
|
||||
.catch( (err) => handleError(err) );
|
||||
}
|
||||
});// end of PTPayment.validate
|
||||
});// end of add event listener submit
|
||||
</script>
|
||||
|
||||
@endsection
|
29
resources/views/vendor/livewire/simple-bootstrap.blade.php
vendored
Normal file
29
resources/views/vendor/livewire/simple-bootstrap.blade.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<div>
|
||||
@if ($paginator->hasPages())
|
||||
<nav>
|
||||
<ul class="pagination">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<li class="page-item disabled" aria-disabled="true">
|
||||
<span class="page-link">@lang('pagination.previous')</span>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item">
|
||||
<button type="button" class="page-link" wire:click="previousPage" wire:loading.attr="disabled" rel="prev">@lang('pagination.previous')</button>
|
||||
</li>
|
||||
@endif
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<li class="page-item">
|
||||
<button type="button" class="page-link" wire:click="nextPage" wire:loading.attr="disabled" rel="next">@lang('pagination.next')</button>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item disabled" aria-disabled="true">
|
||||
<span class="page-link">@lang('pagination.next')</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</nav>
|
||||
@endif
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user