Merge pull request #6317 from turbo124/paytrace

Paytrace
This commit is contained in:
David Bomba 2021-07-23 17:31:23 +10:00 committed by GitHub
commit 5ea07be358
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 990 additions and 10 deletions

View File

@ -74,9 +74,9 @@ class PaymentWebhookRequest extends Request
{
// For testing purposes we'll slow down the webhook processing by 2 seconds
// to make sure webhook request doesn't came before our processing.
if (app()->environment() !== 'production') {
//if (app()->environment() !== 'production') {
sleep(2);
}
//}
// Some gateways, like Checkout, we can dynamically pass payment hash,
// which we will resolve here and get payment information from it.

View File

@ -133,6 +133,7 @@ class PaymentEmailEngine extends BaseEmailEngine
$data['$email'] = ['value' => isset($this->contact) ? $this->contact->email : 'no contact email on record', 'label' => ctrans('texts.email')];
$data['$client_name'] = ['value' => $this->client->present()->name() ?: ' ', 'label' => ctrans('texts.client_name')];
$data['$client.name'] = &$data['$client_name'];
$data['$client'] = &$data['$client_name'];
$data['$client.address1'] = &$data['$address1'];
$data['$client.address2'] = &$data['$address2'];
$data['$client_address'] = ['value' => $this->client->present()->address() ?: ' ', 'label' => ctrans('texts.address')];

View File

@ -60,11 +60,12 @@ class SupportMessageSent extends Mailable
$company = auth()->user()->company();
$user = auth()->user();
$db = str_replace("db-ninja-", "", $company->db);
if(Ninja::isHosted())
$subject = "{$priority}Hosted-{$company->db} :: Customer Support - {$plan} ".date('M jS, g:ia');
$subject = "{$priority}Hosted-{$company->db} :: {$plan} :: ".date('M jS, g:ia');
else
$subject = "{$priority}Self Hosted :: Customer Support - [{$plan}] ".date('M jS, g:ia');
$subject = "{$priority}Self Hosted :: {$plan} :: ".date('M jS, g:ia');
return $this->from(config('mail.from.address'), $user->present()->name())
->replyTo($user->email, $user->present()->name())

View File

@ -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 = [];

View File

@ -81,6 +81,9 @@ class Gateway extends StaticModel
case 1:
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true]];//Authorize.net
break;
case 1:
return [GatewayType::CREDIT_CARD => ['refund' => false, 'token_billing' => false]];//Payfast
break;
case 15:
return [GatewayType::PAYPAL => ['refund' => true, 'token_billing' => false]]; //Paypal
break;
@ -95,6 +98,8 @@ class Gateway extends StaticModel
case 39:
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true]]; //Checkout
break;
case 46:
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true]]; //Paytrace
case 49:
return [GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true],
GatewayType::BANK_TRANSFER => ['refund' => true, 'token_billing' => true]]; //WePay

View File

@ -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;

View File

@ -431,6 +431,61 @@ class BaseDriver extends AbstractPaymentDriver
return false;
}
/*Generic Global unsuccessful transaction method when the client is present*/
public function processUnsuccessfulTransaction($response, $client_present = true)
{
$error = $response['error'];
$error_code = $response['error_code'];
$this->unWindGatewayFees($this->payment_hash);
PaymentFailureMailer::dispatch($this->client, $error, $this->client->company, $this->payment_hash->data->amount_with_fee);
$nmo = new NinjaMailerObject;
$nmo->mailable = new NinjaMailer( (new ClientPaymentFailureObject($this->client, $error, $this->client->company, $this->payment_hash))->build() );
$nmo->company = $this->client->company;
$nmo->settings = $this->client->company->settings;
$invoices = Invoice::whereIn('id', $this->transformKeys(array_column($this->payment_hash->invoices(), 'invoice_id')))->withTrashed()->get();
$invoices->each(function ($invoice){
$invoice->service()->deletePdf();
});
$invoices->first()->invitations->each(function ($invitation) use ($nmo){
if ($invitation->contact->send_email && $invitation->contact->email) {
$nmo->to_user = $invitation->contact;
NinjaMailerJob::dispatch($nmo);
}
});
$message = [
'server_response' => $response,
'data' => $this->payment_hash->data,
];
SystemLogger::dispatch(
$message,
SystemLog::CATEGORY_GATEWAY_RESPONSE,
SystemLog::EVENT_GATEWAY_FAILURE,
$this::SYSTEM_LOG_TYPE,
$this->client,
$this->client->company,
);
if($client_present)
throw new PaymentFailed($error, 500);
}
public function checkRequirements()
{
if ($this->company_gateway->require_billing_address) {

View File

@ -0,0 +1,256 @@
<?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\Invoice;
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 App\Utils\Traits\MakesHash;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
class CreditCard
{
use MakesHash;
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();
$data['gateway'] = $this->paytrace_driver;
return render('gateways.paytrace.authorize', $data);
}
// +"success": true
// +"response_code": 160
// +"status_message": "The customer profile for PLS5U60OoLUfQXzcmtJYNefPA0gTthzT/11 was successfully created."
// +"customer_id": "PLS5U60OoLUfQXzcmtJYNefPA0gTthzT"
//if(!$response->success)
//handle failure
public function authorizeResponse($request)
{
$data = $request->all();
$response = $this->createCustomer($data);
return redirect()->route('client.payment_methods.index');
}
// "_token" => "Vl1xHflBYQt9YFSaNCPTJKlY5x3rwcFE9kvkw71I"
// "company_gateway_id" => "1"
// "HPF_Token" => "e484a92c-90ed-4468-ac4d-da66824c75de"
// "enc_key" => "zqz6HMHCXALWdX5hyBqrIbSwU7TBZ0FTjjLB3Cp0FQY="
// "amount" => "Amount"
// "q" => "/client/payment_methods"
// "method" => "1"
// ]
// "customer_id":"customer789",
// "hpf_token":"e369847e-3027-4174-9161-fa0d4e98d318",
// "enc_key":"lI785yOBMet4Rt9o4NLXEyV84WBU3tdStExcsfoaOoo=",
// "integrator_id":"xxxxxxxxxx",
// "billing_address":{
// "name":"Mark Smith",
// "street_address":"8320 E. West St.",
// "city":"Spokane",
// "state":"WA",
// "zip":"85284"
// }
private function createCustomer($data)
{
$post_data = [
'customer_id' => Str::random(32),
'hpf_token' => $data['HPF_Token'],
'enc_key' => $data['enc_key'],
'integrator_id' => $this->company_gateway->getConfigField('integratorId'),
'billing_address' => $this->buildBillingAddress(),
];
$response = $this->paytrace_driver->gatewayRequest('/v1/customer/pt_protect_create', $post_data);
$cgt = [];
$cgt['token'] = $response->customer_id;
$cgt['payment_method_id'] = GatewayType::CREDIT_CARD;
$profile = $this->getCustomerProfile($response->customer_id);
$payment_meta = new \stdClass;
$payment_meta->exp_month = $profile->credit_card->expiration_month;
$payment_meta->exp_year = $profile->credit_card->expiration_year;
$payment_meta->brand = 'CC';
$payment_meta->last4 = $profile->credit_card->masked_number;
$payment_meta->type = GatewayType::CREDIT_CARD;
$cgt['payment_meta'] = $payment_meta;
$token = $this->paytrace_driver->storeGatewayToken($cgt, []);
return $response;
}
private function getCustomerProfile($customer_id)
{
$profile = $this->paytrace_driver->gatewayRequest('/v1/customer/export', [
'integrator_id' => $this->company_gateway->getConfigField('integratorId'),
'customer_id' => $customer_id,
// 'include_bin' => true,
]);
return $profile->customers[0];
}
private function buildBillingAddress()
{
return [
'name' => $this->paytrace_driver->client->present()->name(),
'street_address' => $this->paytrace_driver->client->address1,
'city' => $this->paytrace_driver->client->city,
'state' => $this->paytrace_driver->client->state,
'zip' => $this->paytrace_driver->client->postal_code
];
}
public function paymentView($data)
{
$data['client_key'] = $this->paytrace_driver->getAuthToken();
$data['gateway'] = $this->paytrace_driver;
return render('gateways.paytrace.pay', $data);
}
public function paymentResponse(Request $request)
{
$response_array = $request->all();
if($request->token)
$this->processTokenPayment($request->token, $request);
if ($request->has('store_card') && $request->input('store_card') === true) {
$response = $this->createCustomer($request->all());
$this->processTokenPayment($response->customer_id, $request);
}
//process a regular charge here:
$data = [
'hpf_token' => $response_array['HPF_Token'],
'enc_key' => $response_array['enc_key'],
'integrator_id' => $this->company_gateway->getConfigField('integratorId'),
'billing_address' => $this->buildBillingAddress(),
'amount' => $request->input('amount_with_fee'),
'invoice_id' => $this->harvestInvoiceId(),
];
$response = $this->paytrace_driver->gatewayRequest('/v1/transactions/sale/pt_protect', $data);
if($response->success)
return $this->processSuccessfulPayment($response);
return $this->processUnsuccessfulPayment($response);
}
public function processTokenPayment($token, $request)
{
$data = [
'customer_id' => $request->token,
'integrator_id' => $this->company_gateway->getConfigField('integratorId'),
'amount' => $request->input('amount_with_fee'),
];
$response = $this->paytrace_driver->gatewayRequest('/v1/transactions/sale/by_customer', $data);
if($response->success){
$this->paytrace_driver->logSuccessfulGatewayResponse(['response' => $response, 'data' => $this->paytrace_driver->payment_hash], SystemLog::TYPE_PAYTRACE);
return $this->processSuccessfulPayment($response);
}
return $this->processUnsuccessfulPayment($response);
}
private function harvestInvoiceId()
{
$_invoice = collect($this->paytrace_driver->payment_hash->data->invoices)->first();
$invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id));
if($invoice)
return ctrans('texts.invoice_number') . "# " . $invoice->number;
return ctrans('texts.invoice_number') . "####";
}
private function processSuccessfulPayment($response)
{
$amount = array_sum(array_column($this->paytrace_driver->payment_hash->invoices(), 'amount')) + $this->paytrace_driver->payment_hash->fee_total;
$payment_record = [];
$payment_record['amount'] = $amount;
$payment_record['payment_type'] = PaymentType::CREDIT_CARD_OTHER;
$payment_record['gateway_type_id'] = GatewayType::CREDIT_CARD;
$payment_record['transaction_reference'] = $response->transaction_id;
$payment = $this->paytrace_driver->createPayment($payment_record, Payment::STATUS_COMPLETED);
return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]);
}
private function processUnsuccessfulPayment($response)
{
$error = $response->status_message;
if(property_exists($response, 'approval_message') && $response->approval_message)
$error .= " - {$response->approval_message}";
$error_code = property_exists($response, 'approval_message') ? $response->approval_message : 'Undefined code';
$data = [
'response' => $response,
'error' => $error,
'error_code' => $error_code,
];
return $this->paytrace_driver->processUnsuccessfulTransaction($data);
}
}

View File

@ -0,0 +1,234 @@
<?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\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\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)
{
// $cgt = ClientGatewayToken::where('company_gateway_id', $payment->company_gateway_id)
// ->where('gateway_type_id', $payment->gateway_type_id)
// ->first();
$data = [
'amount' => $amount,
//'customer_id' => $cgt->token,
'transaction_id' => $payment->transaction_reference,
'integrator_id' => '959195xd1CuC'
];
$response = $this->gatewayRequest('/v1/transactions/refund/for_transaction', $data);
if($response && $response->success)
{
SystemLogger::dispatch(['server_response' => $response, 'data' => $data], SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_SUCCESS, SystemLog::TYPE_PAYTRACE, $this->client, $this->client->company);
return [
'transaction_reference' => $response->transaction_id,
'transaction_response' => json_encode($response),
'success' => true,
'description' => $response->status_message,
'code' => $response->response_code,
];
}
SystemLogger::dispatch(['server_response' => $response, 'data' => $data], SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_PAYTRACE, $this->client, $this->client->company);
return [
'transaction_reference' => null,
'transaction_response' => json_encode($response),
'success' => false,
'description' => $response->status_message,
'code' => 422,
];
}
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
{
$amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total;
$data = [
'customer_id' => $cgt->token,
'integrator_id' => $this->company_gateway->getConfigField('integratorId'),
'amount' => $amount,
];
$response = $this->gatewayRequest('/v1/transactions/sale/by_customer', $data);
if($response && $response->success)
{
$data = [
'gateway_type_id' => $cgt->gateway_type_id,
'payment_type' => PaymentType::CREDIT_CARD_OTHER,
'transaction_reference' => $response->transaction_id,
'amount' => $amount,
];
$payment = $this->createPayment($data);
$payment->meta = $cgt->meta;
$payment->save();
$payment_hash->payment_id = $payment->id;
$payment_hash->save();
return $payment;
}
$error = $response->status_message;
if(property_exists($response, 'approval_message') && $response->approval_message)
$error .= " - {$response->approval_message}";
$data = [
'response' => $response,
'error' => $error,
'error_code' => 500,
];
$this->processUnsuccessfulTransaction($data, false);
}
public function processWebhookRequest(PaymentWebhookRequest $request, Payment $payment = null)
{
}
/*Helpers*/
private function generateAuthHeaders()
{
$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);
$auth_data = json_decode($response);
$headers = [];
$headers[] = 'Content-type: application/json';
$headers[] = 'Authorization: Bearer '.$auth_data->access_token;
return $headers;
}
public function getAuthToken()
{
$headers = $this->generateAuthHeaders();
$response = CurlUtils::post('https://api.paytrace.com/v1/payment_fields/token/create', [], $headers);
$response = json_decode($response);
if($response)
return $response->clientKey;
return false;
}
public function gatewayRequest($uri, $data, $headers = false)
{
$base_url = "https://api.paytrace.com{$uri}";
$headers = $this->generateAuthHeaders();
$response = CurlUtils::post($base_url, json_encode($data), $headers);
$response = json_decode($response);
if($response)
return $response;
return false;
}
}

View File

@ -74,7 +74,7 @@ class Charge
'confirm' => true,
'description' => $description,
];
nlog($data);
$response = $this->stripe->createPaymentIntent($data, $this->stripe->stripe_connect_auth);
// $response = $local_stripe->paymentIntents->create($data);

View File

@ -84,6 +84,10 @@ return [
'test_email' => env('TEST_EMAIL', 'test@example.com'),
'wepay' => env('WEPAY_KEYS', ''),
'braintree' => env('BRAINTREE_KEYS', ''),
'paytrace' => [
'username' => env('PAYTRACE_U', ''),
'password' => env('PAYTRACE_P','')
],
],
'contact' => [
'email' => env('MAIL_FROM_ADDRESS'),

View File

@ -0,0 +1,39 @@
<?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()
{
if($paytrace = Gateway::find(46))
{
$fields = json_decode($paytrace->fields);
$fields->integratorId = "";
$paytrace->fields = json_encode($fields);
$paytrace->provider = 'Paytrace';
$paytrace->visible = true;
$paytrace->save();
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

View File

@ -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":"","integratorId":"","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]);

View File

@ -0,0 +1,145 @@
@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>
<form action="{{ route('client.payment_methods.store', ['method' => App\Models\GatewayType::CREDIT_CARD]) }}" method="post" id="server_response">
@csrf
<div class="w-screen items-center">
<div id='pt_hpf_form'><!--iframe sensitive data payment fields inserted here--></div>
</div>
<input type="hidden" name="company_gateway_id" value="{{ $gateway->company_gateway->id }}">
<input type="txt" id=HPF_Token name= HPF_Token hidden>
<input type="txt" id=enc_key name= enc_key hidden>
<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',
'label_color':'#EF9F6D',
'label_size':'20px',
'background_color':'white',
'border_style':'dotted',
'font_size':'15pt',
'height':'30px',
'width':'100px'
},
'cc': {
'font_color':'#5D99CA',
'border_color':'#EF9F6D',
'label_color':'#EF9F6D',
'label_size':'20px',
'background_color':'white',
'border_style':'solid',
'font_size':'15pt',
'height':'30px',
'width':'300px'
},
'exp': {
'font_color':'#5D99CA',
'border_color':'#EF9F6D',
'label_color':'#EF9F6D',
'label_size':'20px',
'background_color':'white',
'border_style':'dashed',
'font_size':'15pt',
'height':'30px',
'width':'85px',
'type':'dropdown'
}
},
authorization: { 'clientKey': "{!! $client_key !!}" }
}).then(function(instance){
PTPayment.getControl("securityCode").label.text("{!! ctrans('texts.cvv')!!}");
PTPayment.getControl("creditCard").label.text("{!! ctrans('texts.card_number')!!}");
PTPayment.getControl("expiration").label.text("{!! ctrans('texts.expires')!!}");
//PTPayment.style({'cc': {'label_color': 'red'}});
//PTPayment.style({'code': {'label_color': 'red'}});
//PTPayment.style({'exp': {'label_color': 'red'}});
//PTPayment.style({'exp':{'type':'dropdown'}});
//PTPayment.theme('horizontal');
// 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("server_response").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) {
let errors = document.getElementById('errors');
errors.textContent = '';
errors.textContent = validationErrors[0].description;
errors.hidden = false;
} else {
// no error so tokenize
instance.process()
.then( (r) => {
submitPayment(r);
}, (err) => {
handleError(err);
});
}
});
});
});
function handleError(err){
document.write(JSON.stringify(err));
}
function submitPayment(r){
var hpf_token = document.getElementById("HPF_Token");
var enc_key = document.getElementById("enc_key");
hpf_token.value = r.message.hpf_token;
enc_key.value = r.message.enc_key;
document.getElementById("server_response").submit();
}
</script>
@endsection

View File

@ -0,0 +1,207 @@
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title' => ctrans('texts.payment_type_credit_card')])
@section('gateway_head')
@endsection
@section('gateway_content')
<form action="{{ route('client.payments.response') }}" method="post" id="server_response">
@csrf
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
<input type="hidden" name="company_gateway_id" value="{{ $gateway->company_gateway->id }}">
<input type="hidden" name="payment_method_id" value="1">
<input type="hidden" name="token" id="token"/>
<input type="hidden" name="store_card" id="store_card"/>
<input type="hidden" name="amount_with_fee" id="amount_with_fee" value="{{ $total['amount_with_fee'] }}"/>
<input type="txt" id=HPF_Token name= HPF_Token hidden>
<input type="txt" id=enc_key name= enc_key hidden>
<div class="alert alert-failure mb-4" hidden id="errors"></div>
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.payment_type')])
{{ ctrans('texts.credit_card') }}
@endcomponent
@include('portal.ninja2020.gateways.includes.payment_details')
@component('portal.ninja2020.components.general.card-element', ['title' => ctrans('texts.pay_with')])
@if(count($tokens) > 0)
@foreach($tokens as $token)
<label class="mr-4">
<input
type="radio"
data-token="{{ $token->hashed_id }}"
name="payment-type"
class="form-radio cursor-pointer toggle-payment-with-token"/>
<span class="ml-1 cursor-pointer">{{ optional($token->meta)->last4 }}</span>
</label>
@endforeach
@endisset
<label>
<input
type="radio"
id="toggle-payment-with-credit-card"
class="form-radio cursor-pointer"
name="payment-type"
checked/>
<span class="ml-1 cursor-pointer">{{ __('texts.new_card') }}</span>
</label>
@endcomponent
@include('portal.ninja2020.gateways.includes.save_card')
<div class="w-screen items-center" id="paytrace--credit-card-container">
<div id='pt_hpf_form'><!--iframe sensitive data payment fields inserted here--></div>
</div>
@include('portal.ninja2020.gateways.includes.pay_now', ['type' => 'submit'])
</form>
@endsection
@section('gateway_footer')
<script src='https://protect.paytrace.com/js/protect.min.js'></script>
<script>
let token_payment = true;
Array
.from(document.getElementsByClassName('toggle-payment-with-token'))
.forEach((element) => element.addEventListener('click', (e) => {
document
.getElementById('save-card--container').style.display = 'none';
document
.getElementById('paytrace--credit-card-container').style.display = 'none';
document
.getElementById('token').value = e.target.dataset.token;
}));
let payWithCreditCardToggle = document.getElementById('toggle-payment-with-credit-card');
if (payWithCreditCardToggle) {
payWithCreditCardToggle
.addEventListener('click', () => {
document
.getElementById('save-card--container').style.display = 'grid';
document
.getElementById('paytrace--credit-card-container').style.display = 'grid';
document
.getElementById('token').value = null;
token_payment = false;
});
}
var tokens = document.getElementsByClassName('toggle-payment-with-token');
tokens[0].click();
// Minimal Protect.js setup call
PTPayment.setup({
styles:
{
'code': {
'font_color':'#5D99CA',
'border_color':'#EF9F6D',
'label_color':'#EF9F6D',
'label_size':'20px',
'background_color':'white',
'border_style':'dotted',
'font_size':'15pt',
'height':'30px',
'width':'100px'
},
'cc': {
'font_color':'#5D99CA',
'border_color':'#EF9F6D',
'label_color':'#EF9F6D',
'label_size':'20px',
'background_color':'white',
'border_style':'solid',
'font_size':'15pt',
'height':'30px',
'width':'300px'
},
'exp': {
'font_color':'#5D99CA',
'border_color':'#EF9F6D',
'label_color':'#EF9F6D',
'label_size':'20px',
'background_color':'white',
'border_style':'dashed',
'font_size':'15pt',
'height':'30px',
'width':'85px',
'type':'dropdown'
}
},
authorization: { 'clientKey': "{!! $client_key !!}" }
}).then(function(instance){
PTPayment.getControl("securityCode").label.text("{!! ctrans('texts.cvv')!!}");
PTPayment.getControl("creditCard").label.text("{!! ctrans('texts.card_number')!!}");
PTPayment.getControl("expiration").label.text("{!! ctrans('texts.expires')!!}");
document.getElementById("server_response").addEventListener("submit",function(e){
e.preventDefault();
e.stopPropagation();
PTPayment.validate(function(validationErrors) {
if (validationErrors.length >= 1 && !token_payment) {
let errors = document.getElementById('errors');
errors.textContent = '';
errors.textContent = validationErrors[0].description;
errors.hidden = false;
} else {
// no error so tokenize
if(token_payment){
tokenPayment();
}
instance.process()
.then( (r) => {
submitPayment(r);
}, (err) => {
handleError(err);
});
}
});
});
});
function handleError(err){
console.log(err);
document.write(JSON.stringify(err));
}
function submitPayment(r){
var hpf_token = document.getElementById("HPF_Token");
var enc_key = document.getElementById("enc_key");
hpf_token.value = r.message.hpf_token;
enc_key.value = r.message.enc_key;
document.getElementById("server_response").submit();
}
function tokenPayment(){
document.getElementById("server_response").submit();
return false;
}
</script>
@endsection

View 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>