mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 13:12:50 +01:00
Merge pull request #6445 from turbo124/v5-develop
Mollie Payment Gateway
This commit is contained in:
commit
5357731a46
@ -742,6 +742,51 @@ class CreateSingleAccount extends Command
|
||||
$cg->fees_and_limits = $fees_and_limits;
|
||||
$cg->save();
|
||||
}
|
||||
|
||||
|
||||
if (config('ninja.testvars.paytrace.decrypted') && ($this->gateway == 'all' || $this->gateway == 'paytrace')) {
|
||||
$cg = new CompanyGateway;
|
||||
$cg->company_id = $company->id;
|
||||
$cg->user_id = $user->id;
|
||||
$cg->gateway_key = 'bbd736b3254b0aabed6ad7fda1298c88';
|
||||
$cg->require_cvv = true;
|
||||
$cg->require_billing_address = true;
|
||||
$cg->require_shipping_address = true;
|
||||
$cg->update_details = true;
|
||||
$cg->config = encrypt(config('ninja.testvars.paytrace.decrypted'));
|
||||
|
||||
$cg->save();
|
||||
|
||||
|
||||
$gateway_types = $cg->driver(new Client)->gatewayTypes();
|
||||
|
||||
$fees_and_limits = new stdClass;
|
||||
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
|
||||
|
||||
$cg->fees_and_limits = $fees_and_limits;
|
||||
$cg->save();
|
||||
}
|
||||
|
||||
if (config('ninja.testvars.mollie') && ($this->gateway == 'all' || $this->gateway == 'mollie')) {
|
||||
$cg = new CompanyGateway;
|
||||
$cg->company_id = $company->id;
|
||||
$cg->user_id = $user->id;
|
||||
$cg->gateway_key = '1bd651fb213ca0c9d66ae3c336dc77e8';
|
||||
$cg->require_cvv = true;
|
||||
$cg->require_billing_address = true;
|
||||
$cg->require_shipping_address = true;
|
||||
$cg->update_details = true;
|
||||
$cg->config = encrypt(config('ninja.testvars.mollie'));
|
||||
$cg->save();
|
||||
|
||||
$gateway_types = $cg->driver(new Client)->gatewayTypes();
|
||||
|
||||
$fees_and_limits = new stdClass;
|
||||
$fees_and_limits->{$gateway_types[0]} = new FeesAndLimits;
|
||||
|
||||
$cg->fees_and_limits = $fees_and_limits;
|
||||
$cg->save();
|
||||
}
|
||||
}
|
||||
|
||||
private function createRecurringInvoice($client)
|
||||
|
27
app/Http/Controllers/Gateways/Mollie3dsController.php
Normal file
27
app/Http/Controllers/Gateways/Mollie3dsController.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?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\Http\Controllers\Gateways;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Gateways\Mollie\Mollie3dsRequest;
|
||||
use App\Models\PaymentHash;
|
||||
|
||||
class Mollie3dsController extends Controller
|
||||
{
|
||||
public function index(Mollie3dsRequest $request)
|
||||
{
|
||||
return $request->getCompanyGateway()
|
||||
->driver($request->getClient())
|
||||
->process3dsConfirmation($request);
|
||||
}
|
||||
}
|
@ -13,26 +13,14 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\Payments\PaymentWebhookRequest;
|
||||
use App\Libraries\MultiDB;
|
||||
use Auth;
|
||||
|
||||
class PaymentWebhookController extends Controller
|
||||
{
|
||||
public function __invoke(PaymentWebhookRequest $request, string $company_key, string $company_gateway_id)
|
||||
public function __invoke(PaymentWebhookRequest $request)
|
||||
{
|
||||
|
||||
$payment = $request->getPayment();
|
||||
|
||||
if(!$payment)
|
||||
return response()->json(['message' => 'Payment record not found.'], 400);
|
||||
|
||||
$client = is_null($payment) ? $request->getClient() : $payment->client;
|
||||
|
||||
if(!$client)
|
||||
return response()->json(['message' => 'Client record not found.'], 400);
|
||||
|
||||
return $request->getCompanyGateway()
|
||||
->driver($client)
|
||||
->processWebhookRequest($request, $payment);
|
||||
return $request
|
||||
->getCompanyGateway()
|
||||
->driver()
|
||||
->processWebhookRequest($request);
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,11 @@ class PaymentResponseRequest extends FormRequest
|
||||
return PaymentHash::whereRaw('BINARY `hash`= ?', [$input['payment_hash']])->first();
|
||||
}
|
||||
|
||||
public function shouldStoreToken(): bool
|
||||
{
|
||||
return (bool) $this->store_card;
|
||||
}
|
||||
|
||||
public function prepareForValidation()
|
||||
{
|
||||
if ($this->has('store_card')) {
|
||||
|
73
app/Http/Requests/Gateways/Mollie/Mollie3dsRequest.php
Normal file
73
app/Http/Requests/Gateways/Mollie/Mollie3dsRequest.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?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\Http\Requests\Gateways\Mollie;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class Mollie3dsRequest extends FormRequest
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public function getCompany(): ?Company
|
||||
{
|
||||
return Company::where('company_key', $this->company_key)->first();
|
||||
}
|
||||
|
||||
public function getCompanyGateway(): ?CompanyGateway
|
||||
{
|
||||
return CompanyGateway::find($this->decodePrimaryKey($this->company_gateway_id));
|
||||
}
|
||||
|
||||
public function getPaymentHash(): ?PaymentHash
|
||||
{
|
||||
return PaymentHash::where('hash', $this->hash)->first();
|
||||
}
|
||||
|
||||
public function getClient(): ?Client
|
||||
{
|
||||
return Client::find($this->getPaymentHash()->data->client_id);
|
||||
}
|
||||
|
||||
public function getPaymentId(): ?string
|
||||
{
|
||||
return $this->getPaymentHash()->data->payment_id;
|
||||
}
|
||||
}
|
@ -134,7 +134,7 @@ class Request extends FormRequest
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($input['contacts'])) {
|
||||
if (isset($input['contacts']) && is_array($input['contacts'])) {
|
||||
foreach ($input['contacts'] as $key => $contact) {
|
||||
if (array_key_exists('id', $contact) && is_numeric($contact['id'])) {
|
||||
unset($input['contacts'][$key]['id']);
|
||||
|
@ -573,7 +573,7 @@ class CSVImport implements ShouldQueue {
|
||||
}
|
||||
|
||||
private function findUser( $user_hash ) {
|
||||
$user = User::where( 'company_id', $this->company->id )
|
||||
$user = User::where( 'account_id', $this->company->account->id )
|
||||
->where( \DB::raw( 'CONCAT_WS(" ", first_name, last_name)' ), 'like', '%' . $user_hash . '%' )
|
||||
->first();
|
||||
|
||||
|
@ -60,6 +60,7 @@ class AdjustEmailQuota implements ShouldQueue
|
||||
{
|
||||
|
||||
Account::query()->cursor()->each(function ($account){
|
||||
nlog("resetting email quota for {$account->key}");
|
||||
Cache::forget($account->key);
|
||||
Cache::forget("throttle_notified:{$account->key}");
|
||||
});
|
||||
|
@ -175,8 +175,9 @@ class ReminderJob implements ShouldQueue
|
||||
$invoice->line_items = $invoice_items;
|
||||
|
||||
/**Refresh Invoice values*/
|
||||
$invoice = $invoice->calc()->getInvoice();
|
||||
|
||||
$invoice = $invoice->calc()->getInvoice()->save();
|
||||
$invoice->service()->deletePdf();
|
||||
|
||||
nlog("adjusting client balance and invoice balance by ". ($invoice->balance - $temp_invoice_balance));
|
||||
$invoice->client->service()->updateBalance($invoice->balance - $temp_invoice_balance)->save();
|
||||
$invoice->ledger()->updateInvoiceBalance($invoice->balance - $temp_invoice_balance, "Late Fee Adjustment for invoice {$invoice->number}");
|
||||
|
@ -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())
|
||||
|
@ -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 = [];
|
||||
@ -118,7 +122,7 @@ class CompanyGateway extends BaseModel
|
||||
}
|
||||
|
||||
/* This is the public entry point into the payment superclass */
|
||||
public function driver(Client $client)
|
||||
public function driver(Client $client = null)
|
||||
{
|
||||
$class = static::driver_class();
|
||||
|
||||
|
@ -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,16 +98,23 @@ 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
|
||||
break;
|
||||
case 50:
|
||||
return [
|
||||
GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true],
|
||||
GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true], //Braintree
|
||||
GatewayType::PAYPAL => ['refund' => true, 'token_billing' => true]
|
||||
];
|
||||
break;
|
||||
case 7:
|
||||
return [
|
||||
GatewayType::CREDIT_CARD => ['refund' => false, 'token_billing' => true], // Mollie
|
||||
];
|
||||
break;
|
||||
default:
|
||||
return [];
|
||||
break;
|
||||
|
@ -20,7 +20,6 @@ class PaymentHash extends Model
|
||||
protected $casts = [
|
||||
'data' => 'object',
|
||||
];
|
||||
|
||||
|
||||
public function invoices()
|
||||
{
|
||||
@ -41,4 +40,12 @@ class PaymentHash extends Model
|
||||
{
|
||||
return $this->belongsTo(Invoice::class, 'fee_invoice_id', 'id');
|
||||
}
|
||||
|
||||
public function withData(string $property, $value): self
|
||||
{
|
||||
$this->data = array_merge((array) $this->data, [$property => $value]);
|
||||
$this->save();
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -68,8 +68,9 @@ class SystemLog extends Model
|
||||
const TYPE_BRAINTREE = 307;
|
||||
const TYPE_WEPAY = 309;
|
||||
const TYPE_PAYFAST = 310;
|
||||
const TYPE_PAYTRACE = 311;
|
||||
const TYPE_MOLLIE = 312;
|
||||
|
||||
|
||||
const TYPE_QUOTA_EXCEEDED = 400;
|
||||
const TYPE_UPSTREAM_FAILURE = 401;
|
||||
|
||||
|
@ -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) {
|
||||
|
239
app/PaymentDrivers/Mollie/CreditCard.php
Normal file
239
app/PaymentDrivers/Mollie/CreditCard.php
Normal file
@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
namespace App\PaymentDrivers\Mollie;
|
||||
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
|
||||
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\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\PaymentDrivers\MolliePaymentDriver;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class CreditCard
|
||||
{
|
||||
/**
|
||||
* @var MolliePaymentDriver
|
||||
*/
|
||||
protected $mollie;
|
||||
|
||||
public function __construct(MolliePaymentDriver $mollie)
|
||||
{
|
||||
$this->mollie = $mollie;
|
||||
|
||||
$this->mollie->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the page for credit card payments.
|
||||
*
|
||||
* @param array $data
|
||||
* @return Factory|View
|
||||
*/
|
||||
public function paymentView(array $data)
|
||||
{
|
||||
$data['gateway'] = $this->mollie;
|
||||
|
||||
return render('gateways.mollie.credit_card.pay', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a payment object.
|
||||
*
|
||||
* @param PaymentResponseRequest $request
|
||||
* @return mixed
|
||||
*/
|
||||
public function paymentResponse(PaymentResponseRequest $request)
|
||||
{
|
||||
$amount = $this->mollie->convertToMollieAmount((float) $this->mollie->payment_hash->data->amount_with_fee);
|
||||
|
||||
$this->mollie->payment_hash
|
||||
->withData('gateway_type_id', GatewayType::CREDIT_CARD)
|
||||
->withData('client_id', $this->mollie->client->id);
|
||||
|
||||
if (!empty($request->token)) {
|
||||
try {
|
||||
$cgt = ClientGatewayToken::where('token', $request->token)->firstOrFail();
|
||||
|
||||
$payment = $this->mollie->gateway->payments->create([
|
||||
'amount' => [
|
||||
'currency' => $this->mollie->client->currency()->code,
|
||||
'value' => $amount,
|
||||
],
|
||||
'mandateId' => $request->token,
|
||||
'customerId' => $cgt->gateway_customer_reference,
|
||||
'sequenceType' => 'recurring',
|
||||
'description' => \sprintf('Hash: %s', $this->mollie->payment_hash->hash),
|
||||
'webhookUrl' => $this->mollie->company_gateway->webhookUrl(),
|
||||
]);
|
||||
|
||||
if ($payment->status === 'paid') {
|
||||
$this->mollie->logSuccessfulGatewayResponse(
|
||||
['response' => $payment, 'data' => $this->mollie->payment_hash],
|
||||
SystemLog::TYPE_MOLLIE
|
||||
);
|
||||
|
||||
return $this->processSuccessfulPayment($payment);
|
||||
}
|
||||
|
||||
if ($payment->status === 'open') {
|
||||
$this->mollie->payment_hash->withData('payment_id', $payment->id);
|
||||
|
||||
return redirect($payment->getCheckoutUrl());
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
return $this->processUnsuccessfulPayment($e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$data = [
|
||||
'amount' => [
|
||||
'currency' => $this->mollie->client->currency()->code,
|
||||
'value' => $amount,
|
||||
],
|
||||
'description' => \sprintf('Hash: %s', $this->mollie->payment_hash->hash),
|
||||
'redirectUrl' => route('mollie.3ds_redirect', [
|
||||
'company_key' => $this->mollie->client->company->company_key,
|
||||
'company_gateway_id' => $this->mollie->company_gateway->hashed_id,
|
||||
'hash' => $this->mollie->payment_hash->hash,
|
||||
]),
|
||||
'webhookUrl' => $this->mollie->company_gateway->webhookUrl(),
|
||||
'cardToken' => $request->gateway_response,
|
||||
];
|
||||
|
||||
if ($request->shouldStoreToken()) {
|
||||
$customer = $this->mollie->gateway->customers->create([
|
||||
'name' => $this->mollie->client->name,
|
||||
'email' => $this->mollie->client->present()->email(),
|
||||
'metadata' => [
|
||||
'id' => $this->mollie->client->hashed_id,
|
||||
],
|
||||
]);
|
||||
|
||||
$data['customerId'] = $customer->id;
|
||||
$data['sequenceType'] = 'first';
|
||||
|
||||
$this->mollie->payment_hash
|
||||
->withData('mollieCustomerId', $customer->id)
|
||||
->withData('shouldStoreToken', true);
|
||||
}
|
||||
|
||||
$payment = $this->mollie->gateway->payments->create($data);
|
||||
|
||||
if ($payment->status === 'paid') {
|
||||
$this->mollie->logSuccessfulGatewayResponse(
|
||||
['response' => $payment, 'data' => $this->mollie->payment_hash],
|
||||
SystemLog::TYPE_MOLLIE
|
||||
);
|
||||
|
||||
return $this->processSuccessfulPayment($payment);
|
||||
}
|
||||
|
||||
if ($payment->status === 'open') {
|
||||
$this->mollie->payment_hash->withData('payment_id', $payment->id);
|
||||
|
||||
return redirect($payment->getCheckoutUrl());
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$this->processUnsuccessfulPayment($e);
|
||||
|
||||
throw new PaymentFailed($e->getMessage(), $e->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function processSuccessfulPayment(\Mollie\Api\Resources\Payment $payment)
|
||||
{
|
||||
$payment_hash = $this->mollie->payment_hash;
|
||||
|
||||
if (property_exists($payment_hash->data, 'shouldStoreToken') && $payment_hash->data->shouldStoreToken) {
|
||||
try {
|
||||
$mandates = \iterator_to_array($this->mollie->gateway->mandates->listForId($payment_hash->data->mollieCustomerId));
|
||||
} catch (\Mollie\Api\Exceptions\ApiException $e) {
|
||||
return $this->processUnsuccessfulPayment($e);
|
||||
}
|
||||
|
||||
$payment_meta = new \stdClass;
|
||||
$payment_meta->exp_month = (string) $mandates[0]->details->cardExpiryDate;
|
||||
$payment_meta->exp_year = (string) '';
|
||||
$payment_meta->brand = (string) $mandates[0]->details->cardLabel;
|
||||
$payment_meta->last4 = (string) $mandates[0]->details->cardNumber;
|
||||
$payment_meta->type = GatewayType::CREDIT_CARD;
|
||||
|
||||
$this->mollie->storeGatewayToken([
|
||||
'token' => $mandates[0]->id,
|
||||
'payment_method_id' => GatewayType::CREDIT_CARD,
|
||||
'payment_meta' => $payment_meta,
|
||||
], ['gateway_customer_reference' => $payment_hash->data->mollieCustomerId]);
|
||||
}
|
||||
|
||||
$data = [
|
||||
'gateway_type_id' => GatewayType::CREDIT_CARD,
|
||||
'amount' => array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total,
|
||||
'payment_type' => PaymentType::CREDIT_CARD_OTHER,
|
||||
'transaction_reference' => $payment->id,
|
||||
];
|
||||
|
||||
$payment_record = $this->mollie->createPayment($data, Payment::STATUS_COMPLETED);
|
||||
|
||||
SystemLogger::dispatch(
|
||||
['response' => $payment, 'data' => $data],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||
SystemLog::TYPE_MOLLIE,
|
||||
$this->mollie->client,
|
||||
$this->mollie->client->company,
|
||||
);
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->mollie->encodePrimaryKey($payment_record->id)]);
|
||||
}
|
||||
|
||||
public function processUnsuccessfulPayment(\Exception $e)
|
||||
{
|
||||
PaymentFailureMailer::dispatch(
|
||||
$this->mollie->client,
|
||||
$e->getMessage(),
|
||||
$this->mollie->client->company,
|
||||
$this->mollie->payment_hash->data->amount_with_fee
|
||||
);
|
||||
|
||||
SystemLogger::dispatch(
|
||||
$e->getMessage(),
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_MOLLIE,
|
||||
$this->mollie->client,
|
||||
$this->mollie->client->company,
|
||||
);
|
||||
|
||||
throw new PaymentFailed($e->getMessage(), $e->getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Show authorization page.
|
||||
*
|
||||
* @param array $data
|
||||
* @return Factory|View
|
||||
*/
|
||||
public function authorizeView(array $data)
|
||||
{
|
||||
return render('gateways.mollie.credit_card.authorize', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle authorization response.
|
||||
*
|
||||
* @param mixed $request
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function authorizeResponse($request): RedirectResponse
|
||||
{
|
||||
return redirect()->route('client.payment_methods.index');
|
||||
}
|
||||
}
|
354
app/PaymentDrivers/MolliePaymentDriver.php
Normal file
354
app/PaymentDrivers/MolliePaymentDriver.php
Normal file
@ -0,0 +1,354 @@
|
||||
<?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\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
|
||||
use App\Http\Requests\Gateways\Mollie\Mollie3dsRequest;
|
||||
use App\Http\Requests\Payments\PaymentWebhookRequest;
|
||||
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\Mollie\CreditCard;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Mollie\Api\Exceptions\ApiException;
|
||||
use Mollie\Api\MollieApiClient;
|
||||
|
||||
class MolliePaymentDriver extends BaseDriver
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
public $refundable = true;
|
||||
|
||||
/**
|
||||
* @var true
|
||||
*/
|
||||
public $token_billing = true;
|
||||
|
||||
/**
|
||||
* @var true
|
||||
*/
|
||||
public $can_authorise_credit_card = true;
|
||||
|
||||
/**
|
||||
* @var MollieApiClient
|
||||
*/
|
||||
public $gateway;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
public $payment_method;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public static $methods = [
|
||||
GatewayType::CREDIT_CARD => CreditCard::class,
|
||||
];
|
||||
|
||||
const SYSTEM_LOG_TYPE = SystemLog::TYPE_MOLLIE;
|
||||
|
||||
public function init(): self
|
||||
{
|
||||
$this->gateway = new MollieApiClient();
|
||||
|
||||
$this->gateway->setApiKey(
|
||||
$this->company_gateway->getConfigField('apiKey'),
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function gatewayTypes(): array
|
||||
{
|
||||
$types = [];
|
||||
|
||||
$types[] = GatewayType::CREDIT_CARD;
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public function authorizeResponse($request)
|
||||
{
|
||||
return $this->payment_method->authorizeResponse($request);
|
||||
}
|
||||
|
||||
public function processPaymentView(array $data)
|
||||
{
|
||||
return $this->payment_method->paymentView($data);
|
||||
}
|
||||
|
||||
public function processPaymentResponse($request)
|
||||
{
|
||||
return $this->payment_method->paymentResponse($request);
|
||||
}
|
||||
|
||||
public function refund(Payment $payment, $amount, $return_client_response = false)
|
||||
{
|
||||
$this->init();
|
||||
|
||||
try {
|
||||
$payment = $this->gateway->payments->get($payment->transaction_reference);
|
||||
|
||||
$refund = $this->gateway->payments->refund($payment, [
|
||||
'amount' => [
|
||||
'currency' => $this->client->currency()->code,
|
||||
'value' => $this->convertToMollieAmount((float) $amount),
|
||||
],
|
||||
]);
|
||||
|
||||
if ($refund->status === 'refunded') {
|
||||
SystemLogger::dispatch(
|
||||
['server_response' => $refund, 'data' => request()->all()],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||
SystemLog::TYPE_MOLLIE,
|
||||
$this->client,
|
||||
$this->client->company
|
||||
);
|
||||
|
||||
return [
|
||||
'transaction_reference' => $refund->id,
|
||||
'transaction_response' => json_encode($refund),
|
||||
'success' => $refund->status === 'refunded' ? true : false,
|
||||
'description' => $refund->description,
|
||||
'code' => 200,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'transaction_reference' => $refund->id,
|
||||
'transaction_response' => json_encode($refund),
|
||||
'success' => true,
|
||||
'description' => $refund->description,
|
||||
'code' => 0,
|
||||
];
|
||||
} catch (ApiException $e) {
|
||||
SystemLogger::dispatch(
|
||||
['server_response' => $refund, 'data' => request()->all()],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_MOLLIE,
|
||||
$this->client,
|
||||
$this->client->companyk
|
||||
);
|
||||
|
||||
nlog($e->getMessage());
|
||||
|
||||
return [
|
||||
'transaction_reference' => null,
|
||||
'transaction_response' => $e->getMessage(),
|
||||
'success' => false,
|
||||
'description' => $e->getMessage(),
|
||||
'code' => $e->getCode(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
|
||||
{
|
||||
$amount = array_sum(array_column($payment_hash->invoices(), 'amount')) + $payment_hash->fee_total;
|
||||
$invoice = Invoice::whereIn('id', $this->transformKeys(array_column($payment_hash->invoices(), 'invoice_id')))->withTrashed()->first();
|
||||
|
||||
if ($invoice) {
|
||||
$description = "Invoice {$invoice->number} for {$amount} for client {$this->client->present()->name()}";
|
||||
} else {
|
||||
$description = "Payment with no invoice for amount {$amount} for client {$this->client->present()->name()}";
|
||||
}
|
||||
|
||||
$request = new PaymentResponseRequest();
|
||||
$request->setMethod('POST');
|
||||
$request->request->add(['payment_hash' => $payment_hash->hash]);
|
||||
|
||||
$this->init();
|
||||
|
||||
try {
|
||||
$payment = $this->gateway->payments->create([
|
||||
'amount' => [
|
||||
'currency' => $this->client->currency()->code,
|
||||
'value' => $this->convertToMollieAmount($amount),
|
||||
],
|
||||
'mandateId' => $cgt->token,
|
||||
'customerId' => $cgt->gateway_customer_reference,
|
||||
'sequenceType' => 'recurring',
|
||||
'description' => $description,
|
||||
'webhookUrl' => $this->company_gateway->webhookUrl(),
|
||||
]);
|
||||
|
||||
if ($payment->status === 'paid') {
|
||||
$this->confirmGatewayFee($request);
|
||||
|
||||
$data = [
|
||||
'payment_method' => $cgt->token,
|
||||
'payment_type' => PaymentType::CREDIT_CARD_OTHER,
|
||||
'amount' => $amount,
|
||||
'transaction_reference' => $payment->id,
|
||||
'gateway_type_id' => GatewayType::CREDIT_CARD,
|
||||
];
|
||||
|
||||
$payment = $this->createPayment($data, Payment::STATUS_COMPLETED);
|
||||
|
||||
SystemLogger::dispatch(
|
||||
['response' => $payment, 'data' => $data],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||
SystemLog::TYPE_MOLLIE,
|
||||
$this->client
|
||||
);
|
||||
|
||||
return $payment;
|
||||
}
|
||||
|
||||
$this->unWindGatewayFees($payment_hash);
|
||||
|
||||
PaymentFailureMailer::dispatch(
|
||||
$this->client,
|
||||
$payment->details,
|
||||
$this->client->company,
|
||||
$amount
|
||||
);
|
||||
|
||||
$message = [
|
||||
'server_response' => $payment,
|
||||
'data' => $payment_hash->data,
|
||||
];
|
||||
|
||||
SystemLogger::dispatch(
|
||||
$message,
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_CHECKOUT,
|
||||
$this->client
|
||||
);
|
||||
|
||||
return false;
|
||||
} catch (ApiException $e) {
|
||||
$this->unWindGatewayFees($payment_hash);
|
||||
|
||||
$data = [
|
||||
'status' => '',
|
||||
'error_type' => '',
|
||||
'error_code' => $e->getCode(),
|
||||
'param' => '',
|
||||
'message' => $e->getMessage(),
|
||||
];
|
||||
|
||||
SystemLogger::dispatch($data, SystemLog::CATEGORY_GATEWAY_RESPONSE, SystemLog::EVENT_GATEWAY_FAILURE, SystemLog::TYPE_MOLLIE, $this->client, $this->client->company);
|
||||
}
|
||||
}
|
||||
|
||||
public function processWebhookRequest(PaymentWebhookRequest $request)
|
||||
{
|
||||
$validator = Validator::make($request->all(), [
|
||||
'id' => ['required', 'starts_with:tr'],
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json($validator->errors(), 422);
|
||||
}
|
||||
|
||||
$this->init();
|
||||
|
||||
$codes = [
|
||||
'open' => Payment::STATUS_PENDING,
|
||||
'canceled' => Payment::STATUS_CANCELLED,
|
||||
'pending' => Payment::STATUS_PENDING,
|
||||
'expired' => Payment::STATUS_CANCELLED,
|
||||
'failed' => Payment::STATUS_FAILED,
|
||||
'paid' => Payment::STATUS_COMPLETED,
|
||||
];
|
||||
|
||||
try {
|
||||
$payment = $this->gateway->payments->get($request->id);
|
||||
|
||||
$record = Payment::where('transaction_reference', $payment->id)->firstOrFail();
|
||||
$record->status_id = $codes[$payment->status];
|
||||
$record->save();
|
||||
|
||||
return response()->json([], 200);
|
||||
} catch (ApiException $e) {
|
||||
return response()->json(['message' => $e->getMessage(), 'gatewayStatusCode' => $e->getCode()], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function process3dsConfirmation(Mollie3dsRequest $request)
|
||||
{
|
||||
$this->init();
|
||||
|
||||
$this->setPaymentHash($request->getPaymentHash());
|
||||
|
||||
try {
|
||||
$payment = $this->gateway->payments->get($request->getPaymentId());
|
||||
|
||||
return (new CreditCard($this))->processSuccessfulPayment($payment);
|
||||
} catch (\Mollie\Api\Exceptions\ApiException $e) {
|
||||
return (new CreditCard($this))->processUnsuccessfulPayment($e);
|
||||
}
|
||||
}
|
||||
|
||||
public function detach(ClientGatewayToken $token)
|
||||
{
|
||||
$this->init();
|
||||
|
||||
try {
|
||||
$this->gateway->mandates->revokeForId($token->gateway_customer_reference, $token->token);
|
||||
} catch (\Mollie\Api\Exceptions\ApiException $e) {
|
||||
SystemLogger::dispatch(
|
||||
[
|
||||
'server_response' => $e->getMessage(),
|
||||
'data' => request()->all(),
|
||||
],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_MOLLIE,
|
||||
$this->client,
|
||||
$this->client->company
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the amount to the format that Mollie supports.
|
||||
*
|
||||
* @param mixed|float $amount
|
||||
* @return string
|
||||
*/
|
||||
public function convertToMollieAmount($amount): string
|
||||
{
|
||||
return \number_format((float) $amount, 2, '.', '');
|
||||
}
|
||||
}
|
258
app/PaymentDrivers/PayTrace/CreditCard.php
Normal file
258
app/PaymentDrivers/PayTrace/CreditCard.php
Normal file
@ -0,0 +1,258 @@
|
||||
<?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;
|
||||
|
||||
public function __construct(PaytracePaymentDriver $paytrace)
|
||||
{
|
||||
$this->paytrace = $paytrace;
|
||||
}
|
||||
|
||||
public function authorizeView($data)
|
||||
{
|
||||
|
||||
$data['client_key'] = $this->paytrace->getAuthToken();
|
||||
$data['gateway'] = $this->paytrace;
|
||||
|
||||
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->paytrace->company_gateway->getConfigField('integratorId'),
|
||||
'billing_address' => $this->buildBillingAddress(),
|
||||
];
|
||||
|
||||
$response = $this->paytrace->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->storeGatewayToken($cgt, []);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function getCustomerProfile($customer_id)
|
||||
{
|
||||
$profile = $this->paytrace->gatewayRequest('/v1/customer/export', [
|
||||
'integrator_id' => $this->paytrace->company_gateway->getConfigField('integratorId'),
|
||||
'customer_id' => $customer_id,
|
||||
// 'include_bin' => true,
|
||||
]);
|
||||
|
||||
return $profile->customers[0];
|
||||
|
||||
}
|
||||
|
||||
private function buildBillingAddress()
|
||||
{
|
||||
return [
|
||||
'name' => $this->paytrace->client->present()->name(),
|
||||
'street_address' => $this->paytrace->client->address1,
|
||||
'city' => $this->paytrace->client->city,
|
||||
'state' => $this->paytrace->client->state,
|
||||
'zip' => $this->paytrace->client->postal_code
|
||||
];
|
||||
}
|
||||
|
||||
public function paymentView($data)
|
||||
{
|
||||
|
||||
$data['client_key'] = $this->paytrace->getAuthToken();
|
||||
$data['gateway'] = $this->paytrace;
|
||||
|
||||
return render('gateways.paytrace.pay', $data);
|
||||
|
||||
}
|
||||
|
||||
public function paymentResponse(Request $request)
|
||||
{
|
||||
$response_array = $request->all();
|
||||
|
||||
if($request->token){
|
||||
$token = ClientGatewayToken::find($this->decodePrimaryKey($request->token));
|
||||
return $this->processTokenPayment($token->token, $request);
|
||||
}
|
||||
|
||||
if ($request->has('store_card') && $request->input('store_card') === true) {
|
||||
|
||||
$response = $this->createCustomer($request->all());
|
||||
|
||||
return $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->paytrace->company_gateway->getConfigField('integratorId'),
|
||||
'billing_address' => $this->buildBillingAddress(),
|
||||
'amount' => $request->input('amount_with_fee'),
|
||||
'invoice_id' => $this->harvestInvoiceId(),
|
||||
];
|
||||
|
||||
$response = $this->paytrace->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' => $token,
|
||||
'integrator_id' => $this->paytrace->company_gateway->getConfigField('integratorId'),
|
||||
'amount' => $request->input('amount_with_fee'),
|
||||
];
|
||||
|
||||
$response = $this->paytrace->gatewayRequest('/v1/transactions/sale/by_customer', $data);
|
||||
|
||||
if($response->success){
|
||||
$this->paytrace->logSuccessfulGatewayResponse(['response' => $response, 'data' => $this->paytrace->payment_hash], SystemLog::TYPE_PAYTRACE);
|
||||
|
||||
return $this->processSuccessfulPayment($response);
|
||||
}
|
||||
|
||||
return $this->processUnsuccessfulPayment($response);
|
||||
}
|
||||
|
||||
private function harvestInvoiceId()
|
||||
{
|
||||
$_invoice = collect($this->paytrace->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->payment_hash->invoices(), 'amount')) + $this->paytrace->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->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->processUnsuccessfulTransaction($data);
|
||||
|
||||
}
|
||||
|
||||
}
|
234
app/PaymentDrivers/PaytracePaymentDriver.php
Normal file
234
app/PaymentDrivers/PaytracePaymentDriver.php
Normal 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;
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -15,10 +15,14 @@ namespace App\Services\Quote;
|
||||
use App\Factory\CloneQuoteToInvoiceFactory;
|
||||
use App\Models\Quote;
|
||||
use App\Repositories\InvoiceRepository;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class ConvertQuote
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
private $client;
|
||||
|
||||
private $invoice_repo;
|
||||
|
||||
public function __construct($client)
|
||||
@ -34,7 +38,7 @@ class ConvertQuote
|
||||
public function run($quote)
|
||||
{
|
||||
$invoice = CloneQuoteToInvoiceFactory::create($quote, $quote->user_id);
|
||||
$invoice->design_id = $this->client->getSetting('invoice_design_id');
|
||||
$invoice->design_id = $this->decodePrimaryKey($this->client->getSetting('invoice_design_id'));
|
||||
$invoice = $this->invoice_repo->save([], $invoice);
|
||||
|
||||
$invoice->fresh();
|
||||
|
@ -62,6 +62,7 @@
|
||||
"league/omnipay": "^3.1",
|
||||
"livewire/livewire": "^2.4",
|
||||
"maennchen/zipstream-php": "^1.2",
|
||||
"mollie/mollie-api-php": "^2.36",
|
||||
"nwidart/laravel-modules": "^8.0",
|
||||
"omnipay/paypal": "^3.0",
|
||||
"payfast/payfast-php-sdk": "^1.1",
|
||||
|
93
composer.lock
generated
93
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "d2beb37ff5fbee59ad4bb792e944eb10",
|
||||
"content-hash": "275a9dd3910b6ec79607b098406dc6c7",
|
||||
"packages": [
|
||||
{
|
||||
"name": "asm/php-ansible",
|
||||
@ -4386,6 +4386,97 @@
|
||||
},
|
||||
"time": "2019-07-17T11:01:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mollie/mollie-api-php",
|
||||
"version": "v2.36.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mollie/mollie-api-php.git",
|
||||
"reference": "19f69c116d47a3600f0ed629e0df925a43d3a8f5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mollie/mollie-api-php/zipball/19f69c116d47a3600f0ed629e0df925a43d3a8f5",
|
||||
"reference": "19f69c116d47a3600f0ed629e0df925a43d3a8f5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer/ca-bundle": "^1.1",
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-openssl": "*",
|
||||
"php": ">=5.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"eloquent/liberator": "^2.0",
|
||||
"friendsofphp/php-cs-fixer": "^3.0",
|
||||
"guzzlehttp/guzzle": "^6.3 || ^7.0",
|
||||
"phpunit/phpunit": "^5.7 || ^6.5 || ^7.1 || ^8.5"
|
||||
},
|
||||
"suggest": {
|
||||
"mollie/oauth2-mollie-php": "Use OAuth to authenticate with the Mollie API. This is needed for some endpoints. Visit https://docs.mollie.com/ for more information."
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Mollie\\Api\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-2-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mollie B.V.",
|
||||
"email": "info@mollie.com"
|
||||
}
|
||||
],
|
||||
"description": "Mollie API client library for PHP. Mollie is a European Payment Service provider and offers international payment methods such as Mastercard, VISA, American Express and PayPal, and local payment methods such as iDEAL, Bancontact, SOFORT Banking, SEPA direct debit, Belfius Direct Net, KBC Payment Button and various gift cards such as Podiumcadeaukaart and fashioncheque.",
|
||||
"homepage": "https://www.mollie.com/en/developers",
|
||||
"keywords": [
|
||||
"Apple Pay",
|
||||
"CBC",
|
||||
"Przelewy24",
|
||||
"api",
|
||||
"bancontact",
|
||||
"banktransfer",
|
||||
"belfius",
|
||||
"belfius direct net",
|
||||
"charges",
|
||||
"creditcard",
|
||||
"direct debit",
|
||||
"fashioncheque",
|
||||
"gateway",
|
||||
"gift cards",
|
||||
"ideal",
|
||||
"inghomepay",
|
||||
"intersolve",
|
||||
"kbc",
|
||||
"klarna",
|
||||
"mistercash",
|
||||
"mollie",
|
||||
"paylater",
|
||||
"payment",
|
||||
"payments",
|
||||
"paypal",
|
||||
"paysafecard",
|
||||
"podiumcadeaukaart",
|
||||
"recurring",
|
||||
"refunds",
|
||||
"sepa",
|
||||
"service",
|
||||
"sliceit",
|
||||
"sofort",
|
||||
"sofortbanking",
|
||||
"subscriptions"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/mollie/mollie-api-php/issues",
|
||||
"source": "https://github.com/mollie/mollie-api-php/tree/v2.36.1"
|
||||
},
|
||||
"time": "2021-06-23T12:55:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "moneyphp/money",
|
||||
"version": "v3.3.1",
|
||||
|
@ -46,7 +46,7 @@ return [
|
||||
'prefix' => '',
|
||||
'prefix_indexes' => true,
|
||||
'strict' => env('DB_STRICT', false),
|
||||
// 'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',
|
||||
'engine' => 'InnoDB',
|
||||
],
|
||||
|
||||
'sqlite' => [
|
||||
|
@ -84,6 +84,12 @@ 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',''),
|
||||
'decrypted' => env('PAYTRACE_KEYS', ''),
|
||||
],
|
||||
'mollie' => env('MOLLIE_KEYS', ''),
|
||||
],
|
||||
'contact' => [
|
||||
'email' => env('MAIL_FROM_ADDRESS'),
|
||||
|
@ -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()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
<?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
|
||||
*/
|
||||
|
||||
use App\Models\Gateway;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ActivateMolliePaymentDriver extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if($mollie = Gateway::find(7))
|
||||
{
|
||||
$mollie->visible = true;
|
||||
|
||||
$fields = json_decode($mollie->fields);
|
||||
$fields->testMode = false;
|
||||
$fields->profileId = '';
|
||||
|
||||
$mollie->fields = json_encode($fields);
|
||||
|
||||
$mollie->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ class PaymentLibrariesSeeder extends Seeder
|
||||
['id' => 4, 'name' => 'FirstData Connect', 'provider' => 'FirstData_Connect', 'key' => '4e0ed0d34552e6cb433506d1ac03a418', 'fields' => '{"storeId":"","sharedSecret":"","testMode":false}'],
|
||||
['id' => 5, 'name' => 'Migs ThreeParty', 'provider' => 'Migs_ThreeParty', 'key' => '513cdc81444c87c4b07258bc2858d3fa', 'fields' => '{"merchantId":"","merchantAccessCode":"","secureHash":""}'],
|
||||
['id' => 6, 'name' => 'Migs TwoParty', 'provider' => 'Migs_TwoParty', 'key' => '99c2a271b5088951334d1302e038c01a', 'fields' => '{"merchantId":"","merchantAccessCode":"","secureHash":""}'],
|
||||
['id' => 7, 'name' => 'Mollie', 'provider' => 'Mollie', 'is_offsite' => true, 'sort_order' => 8, 'key' => '1bd651fb213ca0c9d66ae3c336dc77e8', 'fields' => '{"apiKey":""}'],
|
||||
['id' => 7, 'name' => 'Mollie', 'provider' => 'Mollie', 'is_offsite' => true, 'sort_order' => 8, 'key' => '1bd651fb213ca0c9d66ae3c336dc77e8', 'fields' => '{"apiKey":"","profileId":"","testMode":false}'],
|
||||
['id' => 8, 'name' => 'MultiSafepay', 'provider' => 'MultiSafepay', 'key' => 'c3dec814e14cbd7d86abd92ce6789f8c', 'fields' => '{"accountId":"","siteId":"","siteCode":"","testMode":false}'],
|
||||
['id' => 9, 'name' => 'Netaxept', 'provider' => 'Netaxept', 'key' => '070dffc5ca94f4e66216e44028ebd52d', 'fields' => '{"merchantId":"","password":"","testMode":false}'],
|
||||
['id' => 10, 'name' => 'NetBanx', 'provider' => 'NetBanx', 'key' => '334d419939c06bd99b4dfd8a49243f0f', 'fields' => '{"accountNumber":"","storeId":"","storePassword":"","testMode":false}'],
|
||||
@ -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,7,15,20,39,46,55,50])->update(['visible' => 1]);
|
||||
|
||||
if (Ninja::isHosted()) {
|
||||
Gateway::whereIn('id', [20])->update(['visible' => 0]);
|
||||
|
1098
package-lock.json
generated
1098
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
2
public/css/app.css
vendored
2
public/css/app.css
vendored
File diff suppressed because one or more lines are too long
2
public/js/clients/payments/mollie-credit-card.js
vendored
Normal file
2
public/js/clients/payments/mollie-credit-card.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/*! For license information please see mollie-credit-card.js.LICENSE.txt */
|
||||
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=22)}({22:function(e,t,n){e.exports=n("i12I")},i12I:function(e,t){function n(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}(new(function(){function e(){var t,n;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.mollie=Mollie(null===(t=document.querySelector("meta[name=mollie-profileId]"))||void 0===t?void 0:t.content,{testmode:null===(n=document.querySelector("meta[name=mollie-testmode]"))||void 0===n?void 0:n.content,locale:"en_US"})}var t,r,o;return t=e,(r=[{key:"createCardHolderInput",value:function(){var e=this.mollie.createComponent("cardHolder");e.mount("#card-holder");var t=document.getElementById("card-holder-error");return e.addEventListener("change",(function(e){e.error&&e.touched?t.textContent=e.error:t.textContent=""})),this}},{key:"createCardNumberInput",value:function(){var e=this.mollie.createComponent("cardNumber");e.mount("#card-number");var t=document.getElementById("card-number-error");return e.addEventListener("change",(function(e){e.error&&e.touched?t.textContent=e.error:t.textContent=""})),this}},{key:"createExpiryDateInput",value:function(){var e=this.mollie.createComponent("expiryDate");e.mount("#expiry-date");var t=document.getElementById("expiry-date-error");return e.addEventListener("change",(function(e){e.error&&e.touched?t.textContent=e.error:t.textContent=""})),this}},{key:"createCvvInput",value:function(){var e=this.mollie.createComponent("verificationCode");e.mount("#cvv");var t=document.getElementById("cvv-error");return e.addEventListener("change",(function(e){e.error&&e.touched?t.textContent=e.error:t.textContent=""})),this}},{key:"handlePayNowButton",value:function(){if(document.getElementById("pay-now").disabled=!0,""!==document.querySelector("input[name=token]").value)return document.querySelector("input[name=gateway_response]").value="",document.getElementById("server-response").submit();this.mollie.createToken().then((function(e){var t=e.token,n=e.error;if(n){document.getElementById("pay-now").disabled=!1;var r=document.getElementById("errors");return r.innerText=n.message,void(r.hidden=!1)}var o=document.querySelector('input[name="token-billing-checkbox"]:checked');o&&(document.querySelector('input[name="store_card"]').value=o.value),document.querySelector("input[name=gateway_response]").value=t,document.querySelector("input[name=token]").value="",document.getElementById("server-response").submit()}))}},{key:"handle",value:function(){var e=this;this.createCardHolderInput().createCardNumberInput().createExpiryDateInput().createCvvInput(),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach((function(e){return e.addEventListener("click",(function(e){document.getElementById("mollie--payment-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=e.target.dataset.token}))})),document.getElementById("toggle-payment-with-credit-card").addEventListener("click",(function(e){document.getElementById("mollie--payment-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value=""})),document.getElementById("pay-now").addEventListener("click",(function(){return e.handlePayNowButton()}))}}])&&n(t.prototype,r),o&&n(t,o),e}())).handle()}});
|
@ -0,0 +1,9 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
2
public/js/clients/payments/paytrace-credit-card.js
vendored
Normal file
2
public/js/clients/payments/paytrace-credit-card.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/*! For license information please see paytrace-credit-card.js.LICENSE.txt */
|
||||
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=21)}({"0Swb":function(e,t){function n(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}(new(function(){function e(){var t;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.clientKey=null===(t=document.querySelector("meta[name=paytrace-client-key]"))||void 0===t?void 0:t.content}var t,r,o;return t=e,(r=[{key:"creditCardStyles",get:function(){return{font_color:"#111827",border_color:"rgba(210,214,220,1)",label_color:"#111827",label_size:"12pt",background_color:"white",border_style:"solid",font_size:"15pt",height:"30px",width:"100%"}}},{key:"codeStyles",get:function(){return{font_color:"#111827",border_color:"rgba(210,214,220,1)",label_color:"#111827",label_size:"12pt",background_color:"white",border_style:"solid",font_size:"15pt",height:"30px",width:"300px"}}},{key:"expStyles",get:function(){return{font_color:"#111827",border_color:"rgba(210,214,220,1)",label_color:"#111827",label_size:"12pt",background_color:"white",border_style:"solid",font_size:"15pt",height:"30px",width:"85px",type:"dropdown"}}},{key:"updatePayTraceLabels",value:function(){window.PTPayment.getControl("securityCode").label.text(document.querySelector("meta[name=ctrans-cvv]").content),window.PTPayment.getControl("creditCard").label.text(document.querySelector("meta[name=ctrans-card_number]").content),window.PTPayment.getControl("expiration").label.text(document.querySelector("meta[name=ctrans-expires]").content)}},{key:"setupPayTrace",value:function(){return window.PTPayment.setup({styles:{code:this.codeStyles,cc:this.creditCardStyles,exp:this.expStyles},authorization:{clientKey:this.clientKey}})}},{key:"handlePaymentWithCreditCard",value:function(e){var t=this;e.target.parentElement.disabled=!0,document.getElementById("errors").hidden=!0,window.PTPayment.validate((function(n){if(n.length>=1){var r=document.getElementById("errors");return r.textContent=n[0].description,r.hidden=!1,e.target.parentElement.disabled=!1}t.ptInstance.process().then((function(e){document.getElementById("HPF_Token").value=e.message.hpf_token,document.getElementById("enc_key").value=e.message.enc_key;var t=document.querySelector('input[name="token-billing-checkbox"]:checked');t&&(document.querySelector('input[name="store_card"]').value=t.value),document.getElementById("server_response").submit()})).catch((function(e){document.getElementById("errors").textContent=JSON.stringify(e),document.getElementById("errors").hidden=!1,console.log(e)}))}))}},{key:"handlePaymentWithToken",value:function(e){e.target.parentElement.disabled=!0,document.getElementById("server_response").submit()}},{key:"handle",value:function(){var e=this;this.setupPayTrace().then((function(t){var n;e.ptInstance=t,e.updatePayTraceLabels(),Array.from(document.getElementsByClassName("toggle-payment-with-token")).forEach((function(e){return e.addEventListener("click",(function(e){document.getElementById("paytrace--credit-card-container").classList.add("hidden"),document.getElementById("save-card--container").style.display="none",document.querySelector("input[name=token]").value=e.target.dataset.token}))})),null===(n=document.getElementById("toggle-payment-with-credit-card"))||void 0===n||n.addEventListener("click",(function(e){document.getElementById("paytrace--credit-card-container").classList.remove("hidden"),document.getElementById("save-card--container").style.display="grid",document.querySelector("input[name=token]").value=""})),document.getElementById("pay-now").addEventListener("click",(function(t){return""===document.querySelector("input[name=token]").value?e.handlePaymentWithCreditCard(t):e.handlePaymentWithToken(t)}))}))}}])&&n(t.prototype,r),o&&n(t,o),e}())).handle()},21:function(e,t,n){e.exports=n("0Swb")}});
|
@ -0,0 +1,9 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"/js/app.js": "/js/app.js?id=696e8203d5e8e7cf5ff5",
|
||||
"/css/app.css": "/css/app.css?id=f4c07fdabcbe50c9f4be",
|
||||
"/css/app.css": "/css/app.css?id=56fdeb0a3b78b00b9a52",
|
||||
"/js/clients/invoices/action-selectors.js": "/js/clients/invoices/action-selectors.js?id=a09bb529b8e1826f13b4",
|
||||
"/js/clients/invoices/payment.js": "/js/clients/invoices/payment.js?id=8ce8955ba775ea5f47d1",
|
||||
"/js/clients/linkify-urls.js": "/js/clients/linkify-urls.js?id=0dc8c34010d09195d2f7",
|
||||
@ -11,6 +11,8 @@
|
||||
"/js/clients/payments/braintree-paypal.js": "/js/clients/payments/braintree-paypal.js?id=c35db3cbb65806ab6a8a",
|
||||
"/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=5469146cd629ea1b5c20",
|
||||
"/js/clients/payments/checkout-credit-card.js": "/js/clients/payments/checkout-credit-card.js?id=065e5450233cc5b47020",
|
||||
"/js/clients/payments/mollie-credit-card.js": "/js/clients/payments/mollie-credit-card.js?id=73b66e88e2daabcd6549",
|
||||
"/js/clients/payments/paytrace-credit-card.js": "/js/clients/payments/paytrace-credit-card.js?id=c8d3808a4c02d1392e96",
|
||||
"/js/clients/payments/stripe-ach.js": "/js/clients/payments/stripe-ach.js?id=81c2623fc1e5769b51c7",
|
||||
"/js/clients/payments/stripe-alipay.js": "/js/clients/payments/stripe-alipay.js?id=665ddf663500767f1a17",
|
||||
"/js/clients/payments/stripe-credit-card.js": "/js/clients/payments/stripe-credit-card.js?id=a30464874dee84678344",
|
||||
@ -21,7 +23,5 @@
|
||||
"/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=5c35d28cf0a3286e7c45",
|
||||
"/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=fc3055d6a099f523ea98",
|
||||
"/js/setup/setup.js": "/js/setup/setup.js?id=8d454e7090f119552a6c",
|
||||
"/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ad",
|
||||
"/js/admin.js": "/js/admin.js",
|
||||
"/css/admin.css": "/css/admin.css"
|
||||
"/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ad"
|
||||
}
|
||||
|
169
resources/js/clients/payments/mollie-credit-card.js
vendored
Normal file
169
resources/js/clients/payments/mollie-credit-card.js
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
class _Mollie {
|
||||
constructor() {
|
||||
this.mollie = Mollie(
|
||||
document.querySelector('meta[name=mollie-profileId]')?.content,
|
||||
{
|
||||
testmode: document.querySelector('meta[name=mollie-testmode]')
|
||||
?.content,
|
||||
locale: 'en_US',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
createCardHolderInput() {
|
||||
let cardHolder = this.mollie.createComponent('cardHolder');
|
||||
cardHolder.mount('#card-holder');
|
||||
|
||||
let cardHolderError = document.getElementById('card-holder-error');
|
||||
|
||||
cardHolder.addEventListener('change', function(event) {
|
||||
if (event.error && event.touched) {
|
||||
cardHolderError.textContent = event.error;
|
||||
} else {
|
||||
cardHolderError.textContent = '';
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
createCardNumberInput() {
|
||||
let cardNumber = this.mollie.createComponent('cardNumber');
|
||||
cardNumber.mount('#card-number');
|
||||
|
||||
let cardNumberError = document.getElementById('card-number-error');
|
||||
|
||||
cardNumber.addEventListener('change', function(event) {
|
||||
if (event.error && event.touched) {
|
||||
cardNumberError.textContent = event.error;
|
||||
} else {
|
||||
cardNumberError.textContent = '';
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
createExpiryDateInput() {
|
||||
let expiryDate = this.mollie.createComponent('expiryDate');
|
||||
expiryDate.mount('#expiry-date');
|
||||
|
||||
let expiryDateError = document.getElementById('expiry-date-error');
|
||||
|
||||
expiryDate.addEventListener('change', function(event) {
|
||||
if (event.error && event.touched) {
|
||||
expiryDateError.textContent = event.error;
|
||||
} else {
|
||||
expiryDateError.textContent = '';
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
createCvvInput() {
|
||||
let verificationCode = this.mollie.createComponent('verificationCode');
|
||||
verificationCode.mount('#cvv');
|
||||
|
||||
let verificationCodeError = document.getElementById('cvv-error');
|
||||
|
||||
verificationCode.addEventListener('change', function(event) {
|
||||
if (event.error && event.touched) {
|
||||
verificationCodeError.textContent = event.error;
|
||||
} else {
|
||||
verificationCodeError.textContent = '';
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
handlePayNowButton() {
|
||||
document.getElementById('pay-now').disabled = true;
|
||||
|
||||
if (document.querySelector('input[name=token]').value !== '') {
|
||||
document.querySelector('input[name=gateway_response]').value = '';
|
||||
|
||||
return document.getElementById('server-response').submit();
|
||||
}
|
||||
|
||||
this.mollie.createToken().then(function(result) {
|
||||
let token = result.token;
|
||||
let error = result.error;
|
||||
|
||||
if (error) {
|
||||
document.getElementById('pay-now').disabled = false;
|
||||
|
||||
let errorsContainer = document.getElementById('errors');
|
||||
errorsContainer.innerText = error.message;
|
||||
errorsContainer.hidden = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let tokenBillingCheckbox = document.querySelector(
|
||||
'input[name="token-billing-checkbox"]:checked'
|
||||
);
|
||||
|
||||
if (tokenBillingCheckbox) {
|
||||
document.querySelector('input[name="store_card"]').value =
|
||||
tokenBillingCheckbox.value;
|
||||
}
|
||||
|
||||
document.querySelector(
|
||||
'input[name=gateway_response]'
|
||||
).value = token;
|
||||
document.querySelector('input[name=token]').value = '';
|
||||
|
||||
document.getElementById('server-response').submit();
|
||||
});
|
||||
}
|
||||
|
||||
handle() {
|
||||
this.createCardHolderInput()
|
||||
.createCardNumberInput()
|
||||
.createExpiryDateInput()
|
||||
.createCvvInput();
|
||||
|
||||
Array.from(
|
||||
document.getElementsByClassName('toggle-payment-with-token')
|
||||
).forEach((element) =>
|
||||
element.addEventListener('click', (element) => {
|
||||
document
|
||||
.getElementById('mollie--payment-container')
|
||||
.classList.add('hidden');
|
||||
document.getElementById('save-card--container').style.display =
|
||||
'none';
|
||||
document.querySelector('input[name=token]').value =
|
||||
element.target.dataset.token;
|
||||
})
|
||||
);
|
||||
|
||||
document
|
||||
.getElementById('toggle-payment-with-credit-card')
|
||||
.addEventListener('click', (element) => {
|
||||
document
|
||||
.getElementById('mollie--payment-container')
|
||||
.classList.remove('hidden');
|
||||
document.getElementById('save-card--container').style.display =
|
||||
'grid';
|
||||
document.querySelector('input[name=token]').value = '';
|
||||
});
|
||||
|
||||
document
|
||||
.getElementById('pay-now')
|
||||
.addEventListener('click', () => this.handlePayNowButton());
|
||||
}
|
||||
}
|
||||
|
||||
new _Mollie().handle();
|
186
resources/js/clients/payments/paytrace-credit-card.js
vendored
Normal file
186
resources/js/clients/payments/paytrace-credit-card.js
vendored
Normal file
@ -0,0 +1,186 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
class PayTraceCreditCard {
|
||||
constructor() {
|
||||
this.clientKey = document.querySelector(
|
||||
'meta[name=paytrace-client-key]'
|
||||
)?.content;
|
||||
}
|
||||
|
||||
get creditCardStyles() {
|
||||
return {
|
||||
font_color: '#111827',
|
||||
border_color: 'rgba(210,214,220,1)',
|
||||
label_color: '#111827',
|
||||
label_size: '12pt',
|
||||
background_color: 'white',
|
||||
border_style: 'solid',
|
||||
font_size: '15pt',
|
||||
height: '30px',
|
||||
width: '100%',
|
||||
};
|
||||
}
|
||||
|
||||
get codeStyles() {
|
||||
return {
|
||||
font_color: '#111827',
|
||||
border_color: 'rgba(210,214,220,1)',
|
||||
label_color: '#111827',
|
||||
label_size: '12pt',
|
||||
background_color: 'white',
|
||||
border_style: 'solid',
|
||||
font_size: '15pt',
|
||||
height: '30px',
|
||||
width: '300px',
|
||||
};
|
||||
}
|
||||
|
||||
get expStyles() {
|
||||
return {
|
||||
font_color: '#111827',
|
||||
border_color: 'rgba(210,214,220,1)',
|
||||
label_color: '#111827',
|
||||
label_size: '12pt',
|
||||
background_color: 'white',
|
||||
border_style: 'solid',
|
||||
font_size: '15pt',
|
||||
height: '30px',
|
||||
width: '85px',
|
||||
type: 'dropdown',
|
||||
};
|
||||
}
|
||||
|
||||
updatePayTraceLabels() {
|
||||
window.PTPayment.getControl('securityCode').label.text(
|
||||
document.querySelector('meta[name=ctrans-cvv]').content
|
||||
);
|
||||
|
||||
window.PTPayment.getControl('creditCard').label.text(
|
||||
document.querySelector('meta[name=ctrans-card_number]').content
|
||||
);
|
||||
|
||||
window.PTPayment.getControl('expiration').label.text(
|
||||
document.querySelector('meta[name=ctrans-expires]').content
|
||||
);
|
||||
}
|
||||
|
||||
setupPayTrace() {
|
||||
return window.PTPayment.setup({
|
||||
styles: {
|
||||
code: this.codeStyles,
|
||||
cc: this.creditCardStyles,
|
||||
exp: this.expStyles,
|
||||
},
|
||||
authorization: {
|
||||
clientKey: this.clientKey,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
handlePaymentWithCreditCard(event) {
|
||||
event.target.parentElement.disabled = true;
|
||||
document.getElementById('errors').hidden = true;
|
||||
|
||||
window.PTPayment.validate((errors) => {
|
||||
if (errors.length >= 1) {
|
||||
let errorsContainer = document.getElementById('errors');
|
||||
|
||||
errorsContainer.textContent = errors[0].description;
|
||||
errorsContainer.hidden = false;
|
||||
|
||||
return (event.target.parentElement.disabled = false);
|
||||
}
|
||||
|
||||
this.ptInstance
|
||||
.process()
|
||||
.then((response) => {
|
||||
document.getElementById('HPF_Token').value =
|
||||
response.message.hpf_token;
|
||||
document.getElementById('enc_key').value =
|
||||
response.message.enc_key;
|
||||
|
||||
let tokenBillingCheckbox = document.querySelector(
|
||||
'input[name="token-billing-checkbox"]:checked'
|
||||
);
|
||||
|
||||
if (tokenBillingCheckbox) {
|
||||
document.querySelector(
|
||||
'input[name="store_card"]'
|
||||
).value = tokenBillingCheckbox.value;
|
||||
}
|
||||
|
||||
document.getElementById('server_response').submit();
|
||||
})
|
||||
.catch((error) => {
|
||||
document.getElementById(
|
||||
'errors'
|
||||
).textContent = JSON.stringify(error);
|
||||
document.getElementById('errors').hidden = false;
|
||||
|
||||
console.log(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
handlePaymentWithToken(event) {
|
||||
event.target.parentElement.disabled = true;
|
||||
|
||||
document.getElementById('server_response').submit();
|
||||
}
|
||||
|
||||
handle() {
|
||||
this.setupPayTrace().then((instance) => {
|
||||
this.ptInstance = instance;
|
||||
this.updatePayTraceLabels();
|
||||
|
||||
Array.from(
|
||||
document.getElementsByClassName('toggle-payment-with-token')
|
||||
).forEach((element) =>
|
||||
element.addEventListener('click', (element) => {
|
||||
document
|
||||
.getElementById('paytrace--credit-card-container')
|
||||
.classList.add('hidden');
|
||||
document.getElementById(
|
||||
'save-card--container'
|
||||
).style.display = 'none';
|
||||
document.querySelector('input[name=token]').value =
|
||||
element.target.dataset.token;
|
||||
})
|
||||
);
|
||||
|
||||
document
|
||||
.getElementById('toggle-payment-with-credit-card')
|
||||
?.addEventListener('click', (element) => {
|
||||
document
|
||||
.getElementById('paytrace--credit-card-container')
|
||||
.classList.remove('hidden');
|
||||
document.getElementById(
|
||||
'save-card--container'
|
||||
).style.display = 'grid';
|
||||
document.querySelector('input[name=token]').value = '';
|
||||
});
|
||||
|
||||
document
|
||||
.getElementById('pay-now')
|
||||
.addEventListener('click', (e) => {
|
||||
if (
|
||||
document.querySelector('input[name=token]').value === ''
|
||||
) {
|
||||
return this.handlePaymentWithCreditCard(e);
|
||||
}
|
||||
|
||||
return this.handlePaymentWithToken(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
new PayTraceCreditCard().handle();
|
@ -4295,6 +4295,7 @@ $LANG = array(
|
||||
'lang_Arabic' => 'Arabic',
|
||||
'lang_Persian' => 'Persian',
|
||||
'lang_Latvian' => 'Latvian',
|
||||
'expiry_date' => 'Expiry date',
|
||||
);
|
||||
|
||||
return $LANG;
|
||||
|
@ -16,12 +16,12 @@
|
||||
value="true"/>
|
||||
<span class="ml-1 cursor-pointer">{{ ctrans('texts.yes') }}</span>
|
||||
</label>
|
||||
<labecoml>
|
||||
<label>
|
||||
<input type="radio" class="form-radio cursor-pointer" name="token-billing-checkbox"
|
||||
id="proxy_is_default"
|
||||
value="false" checked />
|
||||
<span class="ml-1 cursor-pointer">{{ ctrans('texts.no') }}</span>
|
||||
</labecoml>
|
||||
</label>
|
||||
</dd>
|
||||
</div>
|
||||
@else
|
||||
|
@ -0,0 +1,8 @@
|
||||
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.credit_card'), 'card_title' =>
|
||||
ctrans('texts.credit_card')])
|
||||
|
||||
@section('gateway_content')
|
||||
@component('portal.ninja2020.components.general.card-element-single')
|
||||
{{ __('texts.payment_method_cannot_be_authorized_first') }}
|
||||
@endcomponent
|
||||
@endsection
|
@ -0,0 +1,90 @@
|
||||
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.credit_card'), 'card_title' =>
|
||||
ctrans('texts.credit_card')])
|
||||
|
||||
@section('gateway_head')
|
||||
<meta name="mollie-testmode" content="{{ $gateway->company_gateway->getConfigField('testMode') }}">
|
||||
<meta name="mollie-profileId" content="{{ $gateway->company_gateway->getConfigField('profileId') }}">
|
||||
|
||||
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
|
||||
<script src="{{ asset('js/clients/payments/card-js.min.js') }}"></script>
|
||||
|
||||
<link href="{{ asset('css/card-js.min.css') }}" rel="stylesheet" type="text/css">
|
||||
@endsection
|
||||
|
||||
@section('gateway_content')
|
||||
<form action="{{ route('client.payments.response') }}" method="post" id="server-response">
|
||||
@csrf
|
||||
<input type="hidden" name="gateway_response">
|
||||
<input type="hidden" name="store_card">
|
||||
<input type="hidden" name="payment_hash" value="{{ $payment_hash }}">
|
||||
|
||||
<input type="hidden" name="company_gateway_id" value="{{ $gateway->getCompanyGatewayId() }}">
|
||||
<input type="hidden" name="payment_method_id" value="{{ $payment_method_id }}">
|
||||
|
||||
<input type="hidden" name="token">
|
||||
</form>
|
||||
|
||||
<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->token }}" 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
|
||||
@endif
|
||||
|
||||
<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
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element-single')
|
||||
<div class="flex flex-col" id="mollie--payment-container">
|
||||
<label for="card-number">
|
||||
<span class="text-xs text-gray-900 uppercase">{{ ctrans('texts.card_number') }}</span>
|
||||
<div class="input w-full" type="text" id="card-number"></div>
|
||||
<div class="text-xs text-red-500 mt-1 block" id="card-number-error"></div>
|
||||
</label>
|
||||
|
||||
<label for="card-holder" class="block mt-2">
|
||||
<span class="text-xs text-gray-900 uppercase">{{ ctrans('texts.name') }}</span>
|
||||
<div class="input w-full" type="text" id="card-holder"></div>
|
||||
<div class="text-xs text-red-500 mt-1 block" id="card-holder-error"></div>
|
||||
</label>
|
||||
|
||||
<div class="grid grid-cols-12 gap-4 mt-2">
|
||||
<label for="expiry-date" class="col-span-4">
|
||||
<span class="text-xs text-gray-900 uppercase">{{ ctrans('texts.expiry_date') }}</span>
|
||||
<div class="input w-full" type="text" id="expiry-date"></div>
|
||||
<div class="text-xs text-red-500 mt-1 block" id="expiry-date-error"></div>
|
||||
</label>
|
||||
|
||||
<label for="cvv" class="col-span-8">
|
||||
<span class="text-xs text-gray-900 uppercase">{{ ctrans('texts.cvv') }}</span>
|
||||
<div class="input w-full border" type="text" id="cvv"></div>
|
||||
<div class="text-xs text-red-500 mt-1 block" id="cvv-error"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@endcomponent
|
||||
|
||||
@include('portal.ninja2020.gateways.includes.save_card')
|
||||
@include('portal.ninja2020.gateways.includes.pay_now')
|
||||
@endsection
|
||||
|
||||
@section('gateway_footer')
|
||||
<script src="https://js.mollie.com/v1/mollie.js"></script>
|
||||
<script src="{{ asset('js/clients/payments/mollie-credit-card.js') }}"></script>
|
||||
@endsection
|
@ -0,0 +1,37 @@
|
||||
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title'
|
||||
=> ctrans('texts.payment_type_credit_card')])
|
||||
|
||||
@section('gateway_head')
|
||||
<meta name="paytrace-client-key" content="{{ $client_key }}">
|
||||
<meta name="ctrans-cvv" content="{{ ctrans('texts.cvv') }}">
|
||||
<meta name="ctrans-card_number" content="{{ ctrans('texts.card_number') }}">
|
||||
<meta name="ctrans-expires" content="{{ ctrans('texts.expires') }}">
|
||||
@endsection
|
||||
|
||||
@section('gateway_content')
|
||||
<form action="{{ route('client.payment_methods.store', ['method' => App\Models\GatewayType::CREDIT_CARD]) }}"
|
||||
method="post" id="server_response">
|
||||
@csrf
|
||||
<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>
|
||||
<input type="text" name="token" hidden>
|
||||
</form>
|
||||
|
||||
<div class="alert alert-failure mb-4" hidden id="errors"></div>
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element-single')
|
||||
<div class="w-screen items-center" id="paytrace--credit-card-container">
|
||||
<div id="pt_hpf_form"></div>
|
||||
</div>
|
||||
@endcomponent
|
||||
|
||||
@component('portal.ninja2020.gateways.includes.pay_now')
|
||||
{{ ctrans('texts.add_payment_method') }}
|
||||
@endcomponent
|
||||
@endsection
|
||||
|
||||
@section('gateway_footer')
|
||||
<script src="https://protect.paytrace.com/js/protect.min.js"></script>
|
||||
<script src="{{ asset('js/clients/payments/paytrace-credit-card.js') }}"></script>
|
||||
@endsection
|
@ -0,0 +1,64 @@
|
||||
@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title'
|
||||
=> ctrans('texts.payment_type_credit_card')])
|
||||
|
||||
@section('gateway_head')
|
||||
<meta name="paytrace-client-key" content="{{ $client_key }}">
|
||||
<meta name="ctrans-cvv" content="{{ ctrans('texts.cvv') }}">
|
||||
<meta name="ctrans-card_number" content="{{ ctrans('texts.card_number') }}">
|
||||
<meta name="ctrans-expires" content="{{ ctrans('texts.expires') }}">
|
||||
@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>
|
||||
</form>
|
||||
|
||||
<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')
|
||||
|
||||
@component('portal.ninja2020.components.general.card-element-single')
|
||||
<div class="w-screen items-center" id="paytrace--credit-card-container">
|
||||
<div id="pt_hpf_form"></div>
|
||||
</div>
|
||||
@endcomponent
|
||||
|
||||
@include('portal.ninja2020.gateways.includes.pay_now')
|
||||
@endsection
|
||||
|
||||
@section('gateway_footer')
|
||||
<script src='https://protect.paytrace.com/js/protect.min.js'></script>
|
||||
<script src="{{ asset('js/clients/payments/paytrace-credit-card.js') }}"></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>
|
@ -42,3 +42,4 @@ Route::get('stripe/signup/{token}', 'StripeConnectController@initialize')->name(
|
||||
Route::get('stripe/completed', 'StripeConnectController@completed')->name('stripe_connect.return');
|
||||
|
||||
Route::get('checkout/3ds_redirect/{company_key}/{company_gateway_id}/{hash}', 'Gateways\Checkout3dsController@index')->name('checkout.3ds_redirect');
|
||||
Route::get('mollie/3ds_redirect/{company_key}/{company_gateway_id}/{hash}', 'Gateways\Mollie3dsController@index')->name('mollie.3ds_redirect');
|
||||
|
133
tests/Browser/ClientPortal/Gateways/Mollie/CreditCardTest.php
Normal file
133
tests/Browser/ClientPortal/Gateways/Mollie/CreditCardTest.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?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 Tests\Browser\ClientPortal\Gateways\Mollie;
|
||||
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\Browser\Pages\ClientPortal\Login;
|
||||
use Tests\DuskTestCase;
|
||||
|
||||
class CreditCardTest extends DuskTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
foreach (static::$browsers as $browser) {
|
||||
$browser->driver->manage()->deleteAllCookies();
|
||||
}
|
||||
|
||||
// $this->disableCompanyGateways();
|
||||
|
||||
// CompanyGateway::where('gateway_key', '3758e7f7c6f4cecf0f4f348b9a00f456')->restore();
|
||||
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser
|
||||
->visit(new Login())
|
||||
->auth();
|
||||
});
|
||||
}
|
||||
|
||||
public function testPayWithNewCreditCard()
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser
|
||||
->visitRoute('client.invoices.index')
|
||||
->click('@pay-now')
|
||||
->press('Pay Now')
|
||||
->clickLink('Credit Card')
|
||||
->pause(5000)
|
||||
->withinFrame('iframe[name=cardNumber-input]', function (Browser $browser) {
|
||||
$browser->type('#cardNumber', '4242424242424242');
|
||||
})
|
||||
->withinFrame('iframe[name=cardHolder-input]', function (Browser $browser) {
|
||||
$browser->type('#cardHolder', 'Invoice Ninja Test Suite');
|
||||
})
|
||||
->withinFrame('iframe[name=expiryDate-input]', function (Browser $browser) {
|
||||
$browser->type('#expiryDate', '12/29');
|
||||
})
|
||||
->withinFrame('iframe[name=verificationCode-input]', function (Browser $browser) {
|
||||
$browser->type('#verificationCode', '100');
|
||||
})
|
||||
->press('Pay Now')
|
||||
->waitForText('Details of the payment', 60);
|
||||
});
|
||||
}
|
||||
|
||||
public function testPayWithNewCreditCardAndSaveForFutureUse()
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser
|
||||
->visitRoute('client.invoices.index')
|
||||
->click('@pay-now')
|
||||
->press('Pay Now')
|
||||
->clickLink('Credit Card')
|
||||
->pause(5000)
|
||||
->withinFrame('iframe[name=cardNumber-input]', function (Browser $browser) {
|
||||
$browser->type('#cardNumber', '4242424242424242');
|
||||
})
|
||||
->withinFrame('iframe[name=cardHolder-input]', function (Browser $browser) {
|
||||
$browser->type('#cardHolder', 'Invoice Ninja Test Suite');
|
||||
})
|
||||
->withinFrame('iframe[name=expiryDate-input]', function (Browser $browser) {
|
||||
$browser->type('#expiryDate', '12/29');
|
||||
})
|
||||
->withinFrame('iframe[name=verificationCode-input]', function (Browser $browser) {
|
||||
$browser->type('#verificationCode', '100');
|
||||
})
|
||||
->radio('#proxy_is_default', true)
|
||||
->press('Pay Now')
|
||||
->waitForText('Details of the payment', 60)
|
||||
->visitRoute('client.payment_methods.index')
|
||||
->clickLink('View')
|
||||
->assertSee('4242');
|
||||
});
|
||||
}
|
||||
|
||||
public function testPayWithSavedCreditCard()
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser
|
||||
->visitRoute('client.invoices.index')
|
||||
->click('@pay-now')
|
||||
->press('Pay Now')
|
||||
->clickLink('Credit Card')
|
||||
->click('.toggle-payment-with-token')
|
||||
->press('Pay Now')
|
||||
->waitForText('Details of the payment', 60);
|
||||
});
|
||||
}
|
||||
|
||||
public function testAddingPaymentMethodShouldntBePossible()
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser
|
||||
->visitRoute('client.payment_methods.index')
|
||||
->press('Add Payment Method')
|
||||
->clickLink('Credit Card')
|
||||
->assertSee('This payment method can be can saved for future use, once you complete your first transaction. Don\'t forget to check "Store credit card details" during payment process.');
|
||||
});
|
||||
}
|
||||
|
||||
public function testRemoveCreditCard()
|
||||
{
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser
|
||||
->visitRoute('client.payment_methods.index')
|
||||
->clickLink('View')
|
||||
->press('Remove Payment Method')
|
||||
->waitForText('Confirmation')
|
||||
->click('@confirm-payment-removal')
|
||||
->assertSee('Payment method has been successfully removed.');
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
<?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 Tests\Browser\ClientPortal\Gateways\PayTrace;
|
||||
|
||||
use App\Models\CompanyGateway;
|
||||
use Laravel\Dusk\Browser;
|
||||
use Tests\Browser\Pages\ClientPortal\Login;
|
||||
use Tests\DuskTestCase;
|
||||
|
||||
class CreditCardTest extends DuskTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
foreach (static::$browsers as $browser) {
|
||||
$browser->driver->manage()->deleteAllCookies();
|
||||
}
|
||||
|
||||
$this->disableCompanyGateways();
|
||||
|
||||
CompanyGateway::where('gateway_key', 'bbd736b3254b0aabed6ad7fda1298c88')->restore();
|
||||
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser
|
||||
->visit(new Login())
|
||||
->auth();
|
||||
});
|
||||
}
|
||||
|
||||
public function testPayingWithNewCreditCard()
|
||||
{
|
||||
$this->markTestSkipped('Credit card not supported.');
|
||||
|
||||
$this->browse(function (Browser $browser) {
|
||||
$browser
|
||||
->visitRoute('client.invoices.index')
|
||||
->click('@pay-now')
|
||||
->press('Pay Now')
|
||||
->clickLink('Credit Card')
|
||||
->withinFrame('iframe', function (Browser $browser) {
|
||||
$browser
|
||||
->type('CC', '4012000098765439')
|
||||
->select('EXP_MM', '12')
|
||||
->select('EXP_YY', '30')
|
||||
->type('SEC', '999');
|
||||
})
|
||||
->press('Pay Now')
|
||||
->waitForText('Details of the payment', 60);
|
||||
});
|
||||
}
|
||||
}
|
32
tests/Unit/MollieAmountFormatTest.php
Normal file
32
tests/Unit/MollieAmountFormatTest.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?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 Tests\Unit;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class MollieAmountFormatTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @covers \App\PaymentDrivers\MolliePaymentDriver::convertToMollieAmount()
|
||||
*/
|
||||
public function testFormatterIsWorkingCorrectly()
|
||||
{
|
||||
$this->assertEquals('1000.00', \number_format((float) 1000, 2, '.', ''));
|
||||
|
||||
$this->assertEquals('1000.00', \number_format((float) "1000", 2, '.', ''));
|
||||
|
||||
$this->assertEquals('1000.00', \number_format((float) "1000.00", 2, '.', ''));
|
||||
|
||||
$this->assertEquals('1000.00', \number_format((float) "1000.00000", 2, '.', ''));
|
||||
}
|
||||
}
|
8
webpack.mix.js
vendored
8
webpack.mix.js
vendored
@ -81,6 +81,14 @@ mix.js("resources/js/app.js", "public/js")
|
||||
.js(
|
||||
"resources/js/clients/payment_methods/wepay-bank-account.js",
|
||||
"public/js/clients/payment_methods/wepay-bank-account.js"
|
||||
)
|
||||
.js(
|
||||
"resources/js/clients/payments/paytrace-credit-card.js",
|
||||
"public/js/clients/payments/paytrace-credit-card.js"
|
||||
)
|
||||
.js(
|
||||
"resources/js/clients/payments/mollie-credit-card.js",
|
||||
"public/js/clients/payments/mollie-credit-card.js"
|
||||
);
|
||||
|
||||
mix.copyDirectory('node_modules/card-js/card-js.min.css', 'public/css/card-js.min.css');
|
||||
|
Loading…
Reference in New Issue
Block a user