mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-09 20:52:56 +01:00
Merge branch 'powerboard' into v5-develop
This commit is contained in:
commit
b1d92d8354
28
app/Helpers/Sanitizer.php
Normal file
28
app/Helpers/Sanitizer.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
class Sanitizer
|
||||
{
|
||||
|
||||
public static function removeBlanks($input): array
|
||||
{
|
||||
foreach ($input as &$value) {
|
||||
if (is_array($value)) {
|
||||
// Recursively apply the filter to nested arrays
|
||||
$value = self::removeBlanks($value);
|
||||
}
|
||||
}
|
||||
// Use array_filter to remove empty or null values
|
||||
return array_filter($input);
|
||||
}
|
||||
}
|
@ -50,7 +50,7 @@ class InvoiceSummary extends Component
|
||||
|
||||
public function downloadDocument($invoice_hashed_id)
|
||||
{
|
||||
nlog("here");
|
||||
|
||||
$contact = $this->getContext()['contact'];
|
||||
$_invoices = $this->getContext()['invoices'];
|
||||
$i = $_invoices->first(function ($i) use($invoice_hashed_id){
|
||||
@ -61,11 +61,6 @@ class InvoiceSummary extends Component
|
||||
|
||||
$file = (new \App\Jobs\Entity\CreateRawPdf($i->invitations()->where('client_contact_id', $contact->id)->first()))->handle();
|
||||
|
||||
|
||||
nlog("here");
|
||||
|
||||
nlog($file);
|
||||
|
||||
$headers = ['Content-Type' => 'application/pdf'];
|
||||
|
||||
return response()->streamDownload(function () use ($file) {
|
||||
|
@ -28,6 +28,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* @property bool $update_details
|
||||
* @property bool $is_deleted
|
||||
* @property string $config
|
||||
* @property object $settings
|
||||
* @property mixed $fees_and_limits
|
||||
* @property string|null $custom_value1
|
||||
* @property string|null $custom_value2
|
||||
@ -74,6 +75,7 @@ class CompanyGateway extends BaseModel
|
||||
|
||||
protected $casts = [
|
||||
'fees_and_limits' => 'object',
|
||||
'settings' => 'object',
|
||||
'updated_at' => 'timestamp',
|
||||
'created_at' => 'timestamp',
|
||||
'deleted_at' => 'timestamp',
|
||||
@ -156,6 +158,7 @@ class CompanyGateway extends BaseModel
|
||||
'80af24a6a691230bbec33e930ab40666' => 323,
|
||||
'vpyfbmdrkqcicpkjqdusgjfluebftuva' => 324, //BTPay
|
||||
'91be24c7b792230bced33e930ac61676' => 325,
|
||||
'b67581d804dbad1743b61c57285142ad' => 326, //Powerboard
|
||||
];
|
||||
|
||||
protected $touches = [];
|
||||
@ -483,6 +486,18 @@ class CompanyGateway extends BaseModel
|
||||
return $fee;
|
||||
}
|
||||
|
||||
public function getSettings()
|
||||
{
|
||||
// return $this->settings;
|
||||
return $this->settings ?? new \stdClass;
|
||||
}
|
||||
|
||||
public function setSettings($settings)
|
||||
{
|
||||
$this->settings = $settings;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function webhookUrl()
|
||||
{
|
||||
return route('payment_webhook', ['company_key' => $this->company->company_key, 'company_gateway_id' => $this->hashed_id]);
|
||||
|
@ -235,6 +235,10 @@ class Gateway extends StaticModel
|
||||
],
|
||||
GatewayType::ACSS => ['refund' => false, 'token_billing' => true, 'webhooks' => []]
|
||||
]; // Rotessa
|
||||
case 64: //b67581d804dbad1743b61c57285142ad - powerboard
|
||||
return [
|
||||
GatewayType::CREDIT_CARD => ['refund' => true, 'token_billing' => true],
|
||||
];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
|
@ -156,6 +156,8 @@ class SystemLog extends Model
|
||||
|
||||
public const TYPE_ROTESSA = 325;
|
||||
|
||||
public const TYPE_POWERBOARD = 326;
|
||||
|
||||
public const TYPE_QUOTA_EXCEEDED = 400;
|
||||
|
||||
public const TYPE_UPSTREAM_FAILURE = 401;
|
||||
|
@ -410,6 +410,7 @@ class BaseDriver extends AbstractPaymentDriver
|
||||
|
||||
if($invoice && $fee_count == 0){
|
||||
|
||||
|
||||
nlog("apparently no fee, so injecting here!");
|
||||
|
||||
if(!$invoice->uses_inclusive_taxes){ //must account for taxes! ? line item taxes also
|
||||
@ -771,6 +772,18 @@ class BaseDriver extends AbstractPaymentDriver
|
||||
);
|
||||
}
|
||||
|
||||
public function logUnsuccessfulGatewayResponse($response, $gateway_const)
|
||||
{
|
||||
SystemLogger::dispatch(
|
||||
$response,
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
$gateway_const,
|
||||
$this->client,
|
||||
$this->client->company,
|
||||
);
|
||||
}
|
||||
|
||||
public function genericWebhookUrl()
|
||||
{
|
||||
return route('payment_notification_webhook', [
|
||||
|
465
app/PaymentDrivers/CBAPowerBoard/CreditCard.php
Normal file
465
app/PaymentDrivers/CBAPowerBoard/CreditCard.php
Normal file
@ -0,0 +1,465 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\CBAPowerBoard;
|
||||
|
||||
use App\Models\Payment;
|
||||
use App\Models\SystemLog;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\PaymentType;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use Illuminate\Http\Client\RequestException;
|
||||
use App\PaymentDrivers\CBAPowerBoardPaymentDriver;
|
||||
use App\PaymentDrivers\CBAPowerBoard\Models\Charge;
|
||||
use App\PaymentDrivers\Common\LivewireMethodInterface;
|
||||
use App\PaymentDrivers\CBAPowerBoard\Models\PaymentSource;
|
||||
use App\Http\Requests\ClientPortal\Payments\PaymentResponseRequest;
|
||||
use App\PaymentDrivers\CBAPowerBoard\Models\Gateway;
|
||||
|
||||
class CreditCard implements LivewireMethodInterface
|
||||
{
|
||||
private Gateway $cba_gateway;
|
||||
|
||||
public function __construct(public CBAPowerBoardPaymentDriver $powerboard)
|
||||
{
|
||||
$this->cba_gateway = $this->powerboard->settings()->getPaymentGatewayConfiguration(GatewayType::CREDIT_CARD);
|
||||
}
|
||||
|
||||
public function authorizeView(array $data)
|
||||
{
|
||||
$data['payment_method_id'] = GatewayType::CREDIT_CARD;
|
||||
|
||||
$view = $this->powerboard->company_gateway->getConfigField('threeds') ? 'gateways.powerboard.credit_card.authorize' : 'gateways.powerboard.credit_card.authorize_no_3ds';
|
||||
return render($view, $this->paymentData($data));
|
||||
}
|
||||
|
||||
public function authorizeResponse($request)
|
||||
{
|
||||
|
||||
if($request->browser_details)
|
||||
{
|
||||
$payment_source = $this->storePaymentSource($request);
|
||||
|
||||
nlog($payment_source);
|
||||
|
||||
$browser_details = json_decode($request->browser_details, true);
|
||||
|
||||
$payload = [
|
||||
"capture" => false,
|
||||
"amount" => 1,
|
||||
"currency" => $this->powerboard->client->currency()->code,
|
||||
"description" => "Card authorization",
|
||||
"customer" => [
|
||||
"payment_source" => [
|
||||
"vault_token" => $payment_source->vault_token,
|
||||
"gateway_id" => $this->powerboard->settings()->getGatewayId(GatewayType::CREDIT_CARD),
|
||||
],
|
||||
],
|
||||
"_3ds" => [
|
||||
"browser_details" => $browser_details,
|
||||
],
|
||||
];
|
||||
|
||||
nlog($payload);
|
||||
|
||||
$r = $this->powerboard->gatewayRequest('/v1/charges/3ds', (\App\Enum\HttpVerb::POST)->value, $payload, []);
|
||||
|
||||
if ($r->failed()) {
|
||||
return $this->processUnsuccessfulPayment($r);
|
||||
}
|
||||
|
||||
$charge = $r->json();
|
||||
nlog($charge['resource']['data']);
|
||||
|
||||
return response()->json($charge['resource']['data'], 200);
|
||||
|
||||
}
|
||||
elseif($request->charge) {
|
||||
|
||||
$charge_request = json_decode($request->charge, true);
|
||||
nlog("we have the charge request");
|
||||
nlog($charge_request);
|
||||
|
||||
$payload = [
|
||||
'_3ds' => [
|
||||
'id' => $charge_request['charge_3ds_id'],
|
||||
],
|
||||
"capture" => false,
|
||||
"authorization" => true,
|
||||
"amount"=> 1,
|
||||
"currency"=> $this->powerboard->client->currency()->code,
|
||||
"store_cvv"=> true,
|
||||
];
|
||||
|
||||
nlog($payload);
|
||||
|
||||
$r = $this->powerboard->gatewayRequest("/v1/charges", (\App\Enum\HttpVerb::POST)->value, $payload, []);
|
||||
|
||||
if($r->failed()){
|
||||
|
||||
$error_payload = $this->getErrorFromResponse($r);
|
||||
throw new PaymentFailed($error_payload[0], $error_payload[1]);
|
||||
|
||||
}
|
||||
|
||||
$charge = (new \App\PaymentDrivers\CBAPowerBoard\Models\Parse())->encode(Charge::class, $r->object()->resource->data) ?? $r->throw();
|
||||
|
||||
nlog($charge);
|
||||
|
||||
if ($charge->status == 'complete') {
|
||||
|
||||
$this->powerboard->logSuccessfulGatewayResponse(['response' => $charge, 'data' => $this->powerboard->payment_hash], SystemLog::TYPE_POWERBOARD);
|
||||
|
||||
$vt = $charge->customer->payment_source->vault_token;
|
||||
|
||||
$data = [
|
||||
"payment_source" => [
|
||||
"vault_token" => $vt,
|
||||
],
|
||||
];
|
||||
|
||||
$customer = $this->powerboard->customer()->findOrCreateCustomer($data);
|
||||
$cgt = $this->powerboard->customer()->storePaymentMethod($charge->customer->payment_source, $charge->customer);
|
||||
|
||||
return redirect()->route('client.payment_methods.show', ['payment_method' => $cgt->hashed_id]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
elseif($request->charge_no3d){
|
||||
nlog($request->all());
|
||||
|
||||
$payment_source = $this->storePaymentSource($request);
|
||||
|
||||
nlog($payment_source);
|
||||
|
||||
$data = [
|
||||
"payment_source" => [
|
||||
"vault_token" => $payment_source->vault_token,
|
||||
],
|
||||
];
|
||||
|
||||
$customer = $this->powerboard->customer()->findOrCreateCustomer($data);
|
||||
|
||||
$cgt = $this->powerboard->customer()->storePaymentMethod($payment_source, $customer);
|
||||
$cgt->gateway_customer_reference = $this->powerboard->settings()->getGatewayId(GatewayType::CREDIT_CARD);
|
||||
$cgt->save();
|
||||
|
||||
return redirect()->route('client.payment_methods.show', ['payment_method' => $cgt->hashed_id]);
|
||||
|
||||
}
|
||||
|
||||
return redirect()->route('client.payment_methods.index');
|
||||
|
||||
}
|
||||
|
||||
private function getCustomer(): array
|
||||
{
|
||||
$data = [
|
||||
'first_name' => $this->powerboard->client->present()->first_name(),
|
||||
'last_name' => $this->powerboard->client->present()->first_name(),
|
||||
'email' => $this->powerboard->client->present()->email(),
|
||||
// 'phone' => $this->powerboard->client->present()->phone(),
|
||||
// 'type' => 'card',
|
||||
'address_line1' => $this->powerboard->client->address1 ?? '',
|
||||
'address_line2' => $this->powerboard->client->address2 ?? '',
|
||||
'address_state' => $this->powerboard->client->state ?? '',
|
||||
'address_country' => $this->powerboard->client->country->iso_3166_3 ?? '',
|
||||
'address_city' => $this->powerboard->client->city ?? '',
|
||||
'address_postcode' => $this->powerboard->client->postal_code ?? '',
|
||||
];
|
||||
|
||||
return \App\Helpers\Sanitizer::removeBlanks($data);
|
||||
|
||||
}
|
||||
|
||||
private function storePaymentSource($request)
|
||||
{
|
||||
|
||||
$this->powerboard->init();
|
||||
|
||||
$payment_source = $request->gateway_response;
|
||||
|
||||
$payload = array_merge($this->getCustomer(), [
|
||||
'token' => $payment_source,
|
||||
"vault_type" => "permanent",
|
||||
'store_ccv' => true,
|
||||
]);
|
||||
|
||||
nlog($payload);
|
||||
|
||||
$r = $this->powerboard->gatewayRequest('/v1/vault/payment_sources', (\App\Enum\HttpVerb::POST)->value, $payload, []);
|
||||
|
||||
if($r->failed())
|
||||
return $this->powerboard->processInternallyFailedPayment($this->powerboard, $r->throw());
|
||||
|
||||
nlog($r->object());
|
||||
|
||||
$source = (new \App\PaymentDrivers\CBAPowerBoard\Models\Parse())->encode(PaymentSource ::class, $r->object()->resource->data);
|
||||
|
||||
return $source;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function paymentData(array $data): array
|
||||
{
|
||||
if($this->cba_gateway->verification_status != "completed")
|
||||
throw new PaymentFailed("This payment method is not configured as yet. Reference Powerboard portal for further information", 400);
|
||||
|
||||
$merge = [
|
||||
'public_key' => $this->powerboard->company_gateway->getConfigField('publicKey'),
|
||||
'widget_endpoint' => $this->powerboard->widget_endpoint,
|
||||
'gateway' => $this->powerboard,
|
||||
'environment' => $this->powerboard->environment,
|
||||
'gateway_id' => $this->cba_gateway->_id,
|
||||
];
|
||||
|
||||
return array_merge($data, $merge);
|
||||
}
|
||||
|
||||
public function paymentView(array $data)
|
||||
{
|
||||
$data = $this->paymentData($data);
|
||||
|
||||
return render('gateways.powerboard.credit_card.pay', $data);
|
||||
}
|
||||
|
||||
public function livewirePaymentView(array $data): string
|
||||
{
|
||||
return 'gateways.powerboard.credit_card.pay_livewire';
|
||||
}
|
||||
|
||||
public function tokenBilling($request, $cgt, $client_present = false)
|
||||
{
|
||||
|
||||
$payload = [
|
||||
"amount" => $this->powerboard->payment_hash->data->amount_with_fee,
|
||||
"currency" => $this->powerboard->client->currency()->code,
|
||||
"customer" => [
|
||||
"payment_source" => [
|
||||
"vault_token" => $cgt->token,
|
||||
"gateway_id" => $cgt->gateway_customer_reference
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$r = $this->powerboard->gatewayRequest('/v1/charges', (\App\Enum\HttpVerb::POST)->value, $payload, []);
|
||||
|
||||
nlog($r->body());
|
||||
|
||||
if($r->failed()){
|
||||
|
||||
$error_payload = $this->getErrorFromResponse($r);
|
||||
|
||||
throw new PaymentFailed($error_payload[0], $error_payload[1]);
|
||||
|
||||
}
|
||||
|
||||
$charge = (new \App\PaymentDrivers\CBAPowerBoard\Models\Parse())->encode(Charge::class, $r->object()->resource->data) ?? $r->throw();
|
||||
|
||||
nlog($charge);
|
||||
|
||||
$this->powerboard->logSuccessfulGatewayResponse(['response' => $charge, 'data' => $this->powerboard->payment_hash], SystemLog::TYPE_POWERBOARD);
|
||||
|
||||
return $this->processSuccessfulPayment($charge);
|
||||
}
|
||||
|
||||
private function get3dsToken(PaymentSource $source, $request)
|
||||
{
|
||||
|
||||
$payment_hash = PaymentHash::query()->where('hash', $request->payment_hash)->first();
|
||||
|
||||
$browser_details = json_decode($request->browser_details,true);
|
||||
|
||||
$payload = [
|
||||
"amount" => $payment_hash->data->amount_with_fee,
|
||||
"currency" => $this->powerboard->client->currency()->code,
|
||||
"description" => $this->powerboard->getDescription(),
|
||||
"customer" => [
|
||||
"payment_source" => [
|
||||
"vault_token" => $source->vault_token,
|
||||
"gateway_id" => $this->powerboard->settings()->getGatewayId(GatewayType::CREDIT_CARD),
|
||||
],
|
||||
],
|
||||
"_3ds" => [
|
||||
"browser_details" => $browser_details,
|
||||
],
|
||||
];
|
||||
|
||||
nlog($payload);
|
||||
|
||||
$r = $this->powerboard->gatewayRequest('/v1/charges/3ds', (\App\Enum\HttpVerb::POST)->value, $payload, []);
|
||||
|
||||
if ($r->failed()) {
|
||||
return $this->processUnsuccessfulPayment($r);
|
||||
}
|
||||
|
||||
$charge = $r->json();
|
||||
|
||||
return response()->json($charge['resource']['data'], 200);
|
||||
|
||||
}
|
||||
|
||||
public function paymentResponse(PaymentResponseRequest $request)
|
||||
{
|
||||
nlog($request->all());
|
||||
|
||||
$this->powerboard->payment_hash->data = array_merge((array) $this->powerboard->payment_hash->data, ['response' => $request->all()]);
|
||||
$this->powerboard->payment_hash->save();
|
||||
|
||||
$payload = [];
|
||||
|
||||
/** Token Payment */
|
||||
if($request->input('token', false))
|
||||
{
|
||||
$cgt = $this->powerboard
|
||||
->client
|
||||
->gateway_tokens()
|
||||
->where('company_gateway_id', $this->powerboard->company_gateway->id)
|
||||
->where('token', $request->token)
|
||||
->first();
|
||||
|
||||
return $this->tokenBilling($request, $cgt, true);
|
||||
|
||||
}
|
||||
elseif($request->browser_details)
|
||||
{
|
||||
$payment_source = $this->storePaymentSource($request);
|
||||
|
||||
nlog($payment_source);
|
||||
|
||||
return $this->get3dsToken($payment_source, $request);
|
||||
|
||||
}
|
||||
elseif($request->charge) {
|
||||
|
||||
$charge_request = json_decode($request->charge, true);
|
||||
|
||||
nlog($charge_request);
|
||||
|
||||
$payload = [
|
||||
'_3ds' => [
|
||||
'id' => $charge_request['charge_3ds_id'],
|
||||
],
|
||||
"amount"=> $this->powerboard->payment_hash->data->amount_with_fee, //@phpstan-ignore-line
|
||||
"currency"=> $this->powerboard->client->currency()->code,
|
||||
"store_cvv"=> true,
|
||||
];
|
||||
|
||||
$r = $this->powerboard->gatewayRequest("/v1/charges", (\App\Enum\HttpVerb::POST)->value, $payload, []);
|
||||
|
||||
if($r->failed())
|
||||
return $this->processUnsuccessfulPayment($r);
|
||||
|
||||
$charge = (new \App\PaymentDrivers\CBAPowerBoard\Models\Parse())->encode(Charge::class, $r->object()->resource->data) ?? $r->throw();
|
||||
|
||||
nlog($charge);
|
||||
|
||||
if ($charge->status == 'complete') {
|
||||
$this->powerboard->logSuccessfulGatewayResponse(['response' => $charge, 'data' => $this->powerboard->payment_hash], SystemLog::TYPE_POWERBOARD);
|
||||
|
||||
$vt = $charge->customer->payment_source->vault_token;
|
||||
|
||||
if($request->store_card){
|
||||
$data = [
|
||||
"payment_source" => [
|
||||
"vault_token" => $vt,
|
||||
],
|
||||
];
|
||||
|
||||
$customer = $this->powerboard->customer()->findOrCreateCustomer($data);
|
||||
$cgt = $this->powerboard->customer()->storePaymentMethod($charge->customer->payment_source, $charge->customer);
|
||||
}
|
||||
|
||||
return $this->processSuccessfulPayment($charge);
|
||||
}
|
||||
elseif($charge->error){
|
||||
|
||||
$this->powerboard->logUnsuccessfulGatewayResponse($charge, SystemLog::TYPE_POWERBOARD);
|
||||
|
||||
throw new PaymentFailed($charge->error->message, $charge->status);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function processSuccessfulPayment(Charge $charge)
|
||||
{
|
||||
|
||||
$data = [
|
||||
'payment_type' => PaymentType::CREDIT_CARD_OTHER,
|
||||
'amount' => $this->powerboard->payment_hash->data->amount_with_fee,
|
||||
'transaction_reference' => $charge->_id,
|
||||
'gateway_type_id' => GatewayType::CREDIT_CARD,
|
||||
];
|
||||
|
||||
$payment = $this->powerboard->createPayment($data, Payment::STATUS_COMPLETED);
|
||||
|
||||
SystemLogger::dispatch(
|
||||
['response' => $charge, 'data' => $data],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||
SystemLog::TYPE_POWERBOARD,
|
||||
$this->powerboard->client,
|
||||
$this->powerboard->client->company,
|
||||
);
|
||||
|
||||
if ($payment->invoices()->whereHas('subscription')->exists()) {
|
||||
$subscription = $payment->invoices()->first()->subscription;
|
||||
|
||||
if ($subscription && array_key_exists('return_url', $subscription->webhook_configuration) && strlen($subscription->webhook_configuration['return_url']) >= 1) {
|
||||
return redirect($subscription->webhook_configuration['return_url']);
|
||||
}
|
||||
}
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $payment->hashed_id]);
|
||||
}
|
||||
|
||||
private function getErrorFromResponse($response)
|
||||
{
|
||||
|
||||
try {
|
||||
$response->throw();
|
||||
} catch (RequestException $exception) {
|
||||
|
||||
$error_object = $exception->response->object();
|
||||
|
||||
$this->powerboard->logUnsuccessfulGatewayResponse($error_object, SystemLog::TYPE_POWERBOARD);
|
||||
|
||||
$error_message = "Unknown error";
|
||||
|
||||
match($error_object->error->code) {
|
||||
"GatewayError" => $error_message = $error_object->error->message,
|
||||
"UnfulfilledCondition" => $error_message = $error_object->error->message,
|
||||
"transaction_declined" => $error_message = $error_object->error->details[0]->status_code_description,
|
||||
default => $error_message = $error_object->error->message ?? "Unknown error",
|
||||
};
|
||||
|
||||
return [$error_message, $exception->getCode()];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public function processUnsuccessfulPayment($response)
|
||||
{
|
||||
|
||||
$error_payload = $this->getErrorFromResponse($response);
|
||||
|
||||
return response()->json(['message' => $error_payload[0], 'code' => $error_payload[1]], $error_payload[1]);
|
||||
|
||||
}
|
||||
|
||||
}
|
186
app/PaymentDrivers/CBAPowerBoard/Customer.php
Normal file
186
app/PaymentDrivers/CBAPowerBoard/Customer.php
Normal file
@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\CBAPowerBoard;
|
||||
|
||||
use App\Helpers\Sanitizer;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\PaymentDrivers\CBAPowerBoard\Models\Customer as ModelsCustomer;
|
||||
use App\PaymentDrivers\CBAPowerBoard\Models\PaymentSource;
|
||||
use App\PaymentDrivers\CBAPowerBoardPaymentDriver;
|
||||
|
||||
class Customer
|
||||
{
|
||||
public function __construct(public CBAPowerBoardPaymentDriver $powerboard)
|
||||
{
|
||||
}
|
||||
|
||||
public function findOrCreateCustomer(array $customer_data): mixed
|
||||
{
|
||||
$token = $this->powerboard
|
||||
->client
|
||||
->gateway_tokens()
|
||||
->whereNotNull('gateway_customer_reference')
|
||||
->where('company_gateway_id', $this->powerboard->company_gateway->id)
|
||||
->first();
|
||||
|
||||
if($token && $customer = $this->getCustomer($token->gateway_customer_reference)){
|
||||
return (new \App\PaymentDrivers\CBAPowerBoard\Models\Parse())->encode(ModelsCustomer::class, $customer->resource->data);
|
||||
}
|
||||
|
||||
if($customer = $this->findCustomer())
|
||||
return (new \App\PaymentDrivers\CBAPowerBoard\Models\Parse())->encode(ModelsCustomer::class, $customer);
|
||||
|
||||
|
||||
return $this->createCustomer($customer_data);
|
||||
|
||||
}
|
||||
|
||||
public function getCustomer(string $id): mixed
|
||||
{
|
||||
$uri = "/v1/customers/{$id}";
|
||||
|
||||
$r = $this->powerboard->gatewayRequest($uri, (\App\Enum\HttpVerb::GET)->value, [], []);
|
||||
|
||||
nlog($r->json());
|
||||
|
||||
if($r->successful())
|
||||
return $r->object();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function findCustomer(): mixed
|
||||
{
|
||||
$uri = '/v1/customers';
|
||||
|
||||
$query = [
|
||||
'reference' => $this->powerboard->client->client_hash,
|
||||
];
|
||||
|
||||
$r = $this->powerboard->gatewayRequest($uri, (\App\Enum\HttpVerb::GET)->value, $query, []);
|
||||
|
||||
$search_results = $r->object();
|
||||
|
||||
nlog($search_results);
|
||||
|
||||
$customers = $search_results->resource->data;
|
||||
|
||||
return reset($customers); // returns first element or false
|
||||
|
||||
}
|
||||
|
||||
public function createCustomer(array $data = []): object
|
||||
{
|
||||
|
||||
$payload = [
|
||||
'company_name' => $this->powerboard->client->present()->name(),
|
||||
'first_name' => $this->powerboard->client->present()->first_name(),
|
||||
'last_name' => $this->powerboard->client->present()->first_name(),
|
||||
'email' => $this->powerboard->client->present()->email(),
|
||||
'reference' => $this->powerboard->client->client_hash,
|
||||
// 'phone' => $this->powerboard->client->present()->phone(),
|
||||
];
|
||||
|
||||
|
||||
$payload = array_merge($payload, $data);
|
||||
|
||||
$payload = Sanitizer::removeBlanks($payload);
|
||||
|
||||
nlog($payload);
|
||||
|
||||
$uri = "/v1/customers";
|
||||
|
||||
$r = $this->powerboard->gatewayRequest($uri, (\App\Enum\HttpVerb::POST)->value, $payload, []);
|
||||
|
||||
if($r->failed())
|
||||
$r->throw();
|
||||
|
||||
return (new \App\PaymentDrivers\CBAPowerBoard\Models\Parse())->encode(ModelsCustomer::class, $r->object()->resource->data) ?? $r->throw();
|
||||
|
||||
}
|
||||
|
||||
public function storePaymentMethod(?PaymentSource $payment_source = null, ?ModelsCustomer $customer = null): ClientGatewayToken
|
||||
{
|
||||
|
||||
/** @var PaymentSource $source */
|
||||
$source = $payment_source ? $payment_source : end($customer->payment_sources);
|
||||
|
||||
$payment_meta = new \stdClass();
|
||||
$payment_meta->exp_month = (string) $source->expire_month;
|
||||
$payment_meta->exp_year = (string) $source->expire_year;
|
||||
$payment_meta->brand = (string) $source->card_scheme;
|
||||
$payment_meta->last4 = (string) $source->card_number_last4;
|
||||
$payment_meta->gateway_id = $source->gateway_id ?? null;
|
||||
$payment_meta->type = \App\Models\GatewayType::CREDIT_CARD;
|
||||
|
||||
$data = [
|
||||
'payment_meta' => $payment_meta,
|
||||
'token' => $source->vault_token,
|
||||
'payment_method_id' => \App\Models\GatewayType::CREDIT_CARD,
|
||||
];
|
||||
|
||||
$cgt = $this->powerboard->storeGatewayToken($data, ['gateway_customer_reference' => $source->gateway_id]);
|
||||
|
||||
return $cgt;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function addTokenToCustomer(string $token, ModelsCustomer $customer): mixed
|
||||
{
|
||||
nlog("add token to customer");
|
||||
|
||||
$uri = "/v1/customers/{$customer->_id}";
|
||||
|
||||
$payload = [
|
||||
'payment_source' => [
|
||||
'vault_token' => $token,
|
||||
]
|
||||
];
|
||||
|
||||
$r = $this->powerboard->gatewayRequest($uri, (\App\Enum\HttpVerb::POST)->value, $payload, []);
|
||||
|
||||
if($r->failed()){
|
||||
nlog($r->body());
|
||||
return $r->throw();
|
||||
}
|
||||
|
||||
nlog($r->object());
|
||||
|
||||
$customer = (new \App\PaymentDrivers\CBAPowerBoard\Models\Parse())->encode(ModelsCustomer::class, $r->object()->resource->data);
|
||||
|
||||
$source = collect($customer->payment_sources)->first(function (PaymentSource $source) use ($token){
|
||||
return $token == $source->vault_token;
|
||||
});
|
||||
|
||||
nlog("i found the source");
|
||||
nlog($source);
|
||||
|
||||
$cgt = $this->powerboard
|
||||
->client
|
||||
->gateway_tokens()
|
||||
->where('token', $token)
|
||||
->first();
|
||||
|
||||
nlog($cgt->id);
|
||||
|
||||
$meta = $cgt->meta;
|
||||
$meta->gateway_id = $source->gateway_id;
|
||||
$cgt->meta = $meta;
|
||||
$cgt->save();
|
||||
|
||||
return $r->object();
|
||||
}
|
||||
|
||||
}
|
||||
|
84
app/PaymentDrivers/CBAPowerBoard/Models/Charge.php
Normal file
84
app/PaymentDrivers/CBAPowerBoard/Models/Charge.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\CBAPowerBoard\Models;
|
||||
|
||||
class Charge
|
||||
{
|
||||
/** @var ?string */
|
||||
public ?string $external_id;
|
||||
/** @var ?string */
|
||||
public ?string $_id;
|
||||
/** @var ?string */
|
||||
public ?string $created_at;
|
||||
/** @var ?string */
|
||||
public ?string $updated_at;
|
||||
/** @var ?string */
|
||||
public ?string $remittance_date;
|
||||
/** @var ?string */
|
||||
public ?string $company_id;
|
||||
/** @var float */
|
||||
public float $amount;
|
||||
/** @var ?string */
|
||||
public ?string $currency;
|
||||
/** @var ?int */
|
||||
public ?int $__v;
|
||||
/** @var Transaction[] */
|
||||
public array $transactions;
|
||||
/** @var ?bool */
|
||||
public ?bool $one_off;
|
||||
/** @var ?bool */
|
||||
public ?bool $archived;
|
||||
/** @var Customer */
|
||||
public Customer $customer;
|
||||
/** @var ?bool */
|
||||
public ?bool $capture;
|
||||
/** @var ?string */
|
||||
public? string $status;
|
||||
/** @var ?array */
|
||||
public ?array $items;
|
||||
|
||||
public function __construct(
|
||||
?string $external_id,
|
||||
?string $_id,
|
||||
?string $created_at,
|
||||
?string $updated_at,
|
||||
?string $remittance_date,
|
||||
?string $company_id,
|
||||
float $amount,
|
||||
?string $currency,
|
||||
?int $__v,
|
||||
array $transactions,
|
||||
?bool $one_off,
|
||||
?bool $archived,
|
||||
Customer $customer,
|
||||
?bool $capture,
|
||||
?string $status,
|
||||
?array $items,
|
||||
) {
|
||||
$this->external_id = $external_id;
|
||||
$this->_id = $_id;
|
||||
$this->created_at = $created_at;
|
||||
$this->updated_at = $updated_at;
|
||||
$this->remittance_date = $remittance_date;
|
||||
$this->company_id = $company_id;
|
||||
$this->amount = $amount;
|
||||
$this->currency = $currency;
|
||||
$this->__v = $__v;
|
||||
$this->transactions = $transactions;
|
||||
$this->one_off = $one_off;
|
||||
$this->archived = $archived;
|
||||
$this->customer = $customer;
|
||||
$this->capture = $capture;
|
||||
$this->status = $status;
|
||||
$this->items = $items;
|
||||
}
|
||||
}
|
85
app/PaymentDrivers/CBAPowerBoard/Models/Customer.php
Normal file
85
app/PaymentDrivers/CBAPowerBoard/Models/Customer.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\CBAPowerBoard\Models;
|
||||
|
||||
|
||||
class Customer
|
||||
{
|
||||
/** @var ?string */
|
||||
public ?string $_id;
|
||||
/** @var ?string */
|
||||
public ?string $_source_ip_address;
|
||||
/** @var ?string */
|
||||
public ?string $first_name;
|
||||
/** @var ?string */
|
||||
public ?string $last_name;
|
||||
/** @var ?string */
|
||||
public ?string $email;
|
||||
/** @var ?string */
|
||||
public ?string $reference;
|
||||
/** @var ?string */
|
||||
public ?string $default_source;
|
||||
/** @var ?string */
|
||||
public ?string $status;
|
||||
/** @var ?bool */
|
||||
public ?bool $archived;
|
||||
/** @var ?string */
|
||||
public ?string $created_at;
|
||||
/** @var ?string */
|
||||
public ?string $updated_at;
|
||||
/** @var ?bool */
|
||||
public ?bool $_check_expire_date;
|
||||
/** @var ?PaymentSource[] */
|
||||
public ?array $payment_sources;
|
||||
/** @var ?PaymentSource */
|
||||
public ?PaymentSource $payment_source;
|
||||
/** @var ?array */
|
||||
public ?array $payment_destinations;
|
||||
/** @var ?string */
|
||||
public ?string $company_id;
|
||||
|
||||
public function __construct(
|
||||
?string $_id,
|
||||
?string $_source_ip_address,
|
||||
?string $first_name,
|
||||
?string $last_name,
|
||||
?string $email,
|
||||
?string $reference,
|
||||
?string $default_source,
|
||||
?string $status,
|
||||
?bool $archived,
|
||||
?string $created_at,
|
||||
?string $updated_at,
|
||||
?bool $_check_expire_date,
|
||||
?array $payment_sources,
|
||||
?array $payment_destinations,
|
||||
?string $company_id,
|
||||
?PaymentSource $payment_source
|
||||
) {
|
||||
$this->_id = $_id;
|
||||
$this->_source_ip_address = $_source_ip_address;
|
||||
$this->first_name = $first_name;
|
||||
$this->last_name = $last_name;
|
||||
$this->email = $email;
|
||||
$this->reference = $reference;
|
||||
$this->default_source = $default_source;
|
||||
$this->status = $status;
|
||||
$this->archived = $archived;
|
||||
$this->created_at = $created_at;
|
||||
$this->updated_at = $updated_at;
|
||||
$this->_check_expire_date = $_check_expire_date;
|
||||
$this->payment_sources = $payment_sources;
|
||||
$this->payment_destinations = $payment_destinations;
|
||||
$this->company_id = $company_id;
|
||||
$this->payment_source = $payment_source;
|
||||
}
|
||||
}
|
56
app/PaymentDrivers/CBAPowerBoard/Models/Gateway.php
Normal file
56
app/PaymentDrivers/CBAPowerBoard/Models/Gateway.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\CBAPowerBoard\Models;
|
||||
|
||||
class Gateway
|
||||
{
|
||||
/** @var string */
|
||||
public string $_id;
|
||||
/** @var string */
|
||||
public string $name;
|
||||
/** @var string */
|
||||
public string $type;
|
||||
/** @var string */
|
||||
public string $mode;
|
||||
/** @var string */
|
||||
public string $created_at;
|
||||
/** @var string */
|
||||
public string $updated_at;
|
||||
/** @var bool */
|
||||
public bool $archived;
|
||||
/** @var bool */
|
||||
public bool $default;
|
||||
/** @var string */
|
||||
public string $verification_status;
|
||||
|
||||
public function __construct(
|
||||
string $_id,
|
||||
string $name,
|
||||
string $type,
|
||||
string $mode,
|
||||
string $created_at,
|
||||
string $updated_at,
|
||||
bool $archived,
|
||||
bool $default,
|
||||
string $verification_status
|
||||
) {
|
||||
$this->_id = $_id;
|
||||
$this->name = $name;
|
||||
$this->type = $type;
|
||||
$this->mode = $mode;
|
||||
$this->created_at = $created_at;
|
||||
$this->updated_at = $updated_at;
|
||||
$this->archived = $archived;
|
||||
$this->default = $default;
|
||||
$this->verification_status = $verification_status;
|
||||
}
|
||||
}
|
70
app/PaymentDrivers/CBAPowerBoard/Models/Parse.php
Normal file
70
app/PaymentDrivers/CBAPowerBoard/Models/Parse.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\CBAPowerBoard\Models;
|
||||
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
||||
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
|
||||
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
|
||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
|
||||
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
|
||||
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
|
||||
use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter;
|
||||
|
||||
class Parse
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function encode($object_type, $document)
|
||||
{
|
||||
|
||||
$phpDocExtractor = new PhpDocExtractor();
|
||||
$reflectionExtractor = new ReflectionExtractor();
|
||||
|
||||
// list of PropertyTypeExtractorInterface (any iterable)
|
||||
$typeExtractors = [$reflectionExtractor,$phpDocExtractor];
|
||||
|
||||
// list of PropertyDescriptionExtractorInterface (any iterable)
|
||||
$descriptionExtractors = [$phpDocExtractor];
|
||||
|
||||
// list of PropertyInitializableExtractorInterface (any iterable)
|
||||
$propertyInitializableExtractors = [$reflectionExtractor];
|
||||
|
||||
$propertyInfo = new PropertyInfoExtractor(
|
||||
$propertyInitializableExtractors,
|
||||
$descriptionExtractors,
|
||||
$typeExtractors,
|
||||
);
|
||||
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
|
||||
|
||||
$metadataAwareNameConverter = new MetadataAwareNameConverter($classMetadataFactory);
|
||||
|
||||
$normalizer = new ObjectNormalizer($classMetadataFactory, $metadataAwareNameConverter, null, $propertyInfo);
|
||||
|
||||
$normalizers = [new DateTimeNormalizer(), $normalizer, new ArrayDenormalizer()];
|
||||
|
||||
$encoders = [new JsonEncoder()];
|
||||
|
||||
$serializer = new Serializer($normalizers, $encoders);
|
||||
|
||||
$data = $serializer->deserialize(json_encode($document), $object_type, 'json', [\Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer::SKIP_NULL_VALUES => true]);
|
||||
|
||||
return $data;
|
||||
|
||||
}
|
||||
}
|
96
app/PaymentDrivers/CBAPowerBoard/Models/PaymentSource.php
Normal file
96
app/PaymentDrivers/CBAPowerBoard/Models/PaymentSource.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\CBAPowerBoard\Models;
|
||||
|
||||
class PaymentSource
|
||||
{
|
||||
/** @var ?string */
|
||||
public ?string $_id;
|
||||
/** @var string */
|
||||
public string $type;
|
||||
/** @var string */
|
||||
public string $vault_token;
|
||||
/** @var string */
|
||||
public string $card_name;
|
||||
/** @var string */
|
||||
public string $card_number_bin;
|
||||
/** @var string */
|
||||
public string $card_number_last4;
|
||||
/** @var string */
|
||||
public string $card_scheme;
|
||||
/** @var string|null */
|
||||
public ?string $address_line1;
|
||||
/** @var string|null */
|
||||
public ?string $address_line2;
|
||||
/** @var string|null */
|
||||
public ?string $address_city;
|
||||
/** @var string|null */
|
||||
public ?string $address_country;
|
||||
/** @var string|null */
|
||||
public ?string $address_state;
|
||||
/** @var int */
|
||||
public int $expire_month;
|
||||
/** @var int */
|
||||
public int $expire_year;
|
||||
/** @var ?string */
|
||||
public ?string $status;
|
||||
/** @var ?string */
|
||||
public ?string $created_at;
|
||||
/** @var ?string */
|
||||
public ?string $updated_at;
|
||||
/** @var ?string */
|
||||
public ?string $vault_type;
|
||||
/** @var ?string */
|
||||
public ?string $gateway_id;
|
||||
|
||||
public function __construct(
|
||||
?string $_id,
|
||||
string $type,
|
||||
string $vault_token,
|
||||
string $card_name,
|
||||
string $card_number_bin,
|
||||
string $card_number_last4,
|
||||
string $card_scheme,
|
||||
?string $address_line1,
|
||||
?string $address_line2,
|
||||
?string $address_city,
|
||||
?string $address_country,
|
||||
?string $address_state,
|
||||
int $expire_month,
|
||||
int $expire_year,
|
||||
?string $status,
|
||||
?string $created_at,
|
||||
?string $updated_at,
|
||||
?string $vault_type,
|
||||
?string $gateway_id
|
||||
) {
|
||||
$this->_id = $_id;
|
||||
$this->type = $type;
|
||||
$this->vault_token = $vault_token;
|
||||
$this->card_name = $card_name;
|
||||
$this->card_number_bin = $card_number_bin;
|
||||
$this->card_number_last4 = $card_number_last4;
|
||||
$this->card_scheme = $card_scheme;
|
||||
$this->address_line1 = $address_line1;
|
||||
$this->address_line2 = $address_line2;
|
||||
$this->address_city = $address_city;
|
||||
$this->address_country = $address_country;
|
||||
$this->address_state = $address_state;
|
||||
$this->expire_month = $expire_month;
|
||||
$this->expire_year = $expire_year;
|
||||
$this->status = $status;
|
||||
$this->created_at = $created_at;
|
||||
$this->updated_at = $updated_at;
|
||||
$this->vault_type = $vault_type;
|
||||
$this->gateway_id = $gateway_id;
|
||||
}
|
||||
}
|
18
app/PaymentDrivers/CBAPowerBoard/Models/PaymentSources.php
Normal file
18
app/PaymentDrivers/CBAPowerBoard/Models/PaymentSources.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\CBAPowerBoard\Models;
|
||||
|
||||
class PaymentSources
|
||||
{
|
||||
/** @var \App\PaymentDrivers\CBAPowerBoard\Models\PaymentSources[] */
|
||||
public array $payment_sources;
|
||||
}
|
79
app/PaymentDrivers/CBAPowerBoard/Models/Transaction.php
Normal file
79
app/PaymentDrivers/CBAPowerBoard/Models/Transaction.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\CBAPowerBoard\Models;
|
||||
|
||||
class Threeds
|
||||
{
|
||||
public function __construct(public ?string $token){}
|
||||
}
|
||||
|
||||
class Transaction
|
||||
{
|
||||
public ?Threeds $_3ds;
|
||||
public ?string $gateway_specific_code;
|
||||
public ?string $gateway_specific_description;
|
||||
public ?string $error_message;
|
||||
public ?string $error_code;
|
||||
public ?string $status_code;
|
||||
public ?string $status_code_description;
|
||||
public ?string $type;
|
||||
public ?string $status;
|
||||
public float $amount;
|
||||
public ?string $currency;
|
||||
public ?string $_id;
|
||||
public ?string $created_at;
|
||||
public ?string $updated_at;
|
||||
public ?string $processed_at;
|
||||
public ?string $external_id;
|
||||
public ?string $external_reference;
|
||||
public ?string $authorization_code;
|
||||
|
||||
public function __construct(
|
||||
?Threeds $_3ds,
|
||||
?string $gateway_specific_code,
|
||||
?string $gateway_specific_description,
|
||||
?string $error_message,
|
||||
?string $error_code,
|
||||
?string $status_code,
|
||||
?string $status_code_description,
|
||||
?string $type,
|
||||
?string $status,
|
||||
float $amount,
|
||||
?string $currency,
|
||||
?string $_id,
|
||||
?string $created_at,
|
||||
?string $updated_at,
|
||||
?string $processed_at,
|
||||
?string $external_id,
|
||||
?string $external_reference,
|
||||
?string $authorization_code
|
||||
) {
|
||||
$this->_3ds = $_3ds;
|
||||
$this->gateway_specific_code = $gateway_specific_code;
|
||||
$this->gateway_specific_description = $gateway_specific_description;
|
||||
$this->error_message = $error_message;
|
||||
$this->error_code = $error_code;
|
||||
$this->status_code = $status_code;
|
||||
$this->status_code_description = $status_code_description;
|
||||
$this->type = $type;
|
||||
$this->status = $status;
|
||||
$this->amount = $amount;
|
||||
$this->currency = $currency;
|
||||
$this->_id = $_id;
|
||||
$this->created_at = $created_at;
|
||||
$this->updated_at = $updated_at;
|
||||
$this->processed_at = $processed_at;
|
||||
$this->external_id = $external_id;
|
||||
$this->external_reference = $external_reference;
|
||||
$this->authorization_code = $authorization_code;
|
||||
}
|
||||
}
|
96
app/PaymentDrivers/CBAPowerBoard/Settings.php
Normal file
96
app/PaymentDrivers/CBAPowerBoard/Settings.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\CBAPowerBoard;
|
||||
|
||||
|
||||
use App\PaymentDrivers\CBAPowerBoard\Models\Gateways;
|
||||
|
||||
use App\PaymentDrivers\CBAPowerBoard\Models\Gateway;
|
||||
use App\PaymentDrivers\CBAPowerBoardPaymentDriver;
|
||||
|
||||
class Settings
|
||||
{
|
||||
protected const GATEWAY_CBA = 'MasterCard';
|
||||
protected const GATEWAY_AFTERPAY = 'Afterpay';
|
||||
protected const GATEWAY_PAYPAL = 'Paypal';
|
||||
protected const GATEWAY_ZIP = 'Zipmoney';
|
||||
|
||||
public function __construct(public CBAPowerBoardPaymentDriver $powerboard)
|
||||
{
|
||||
}
|
||||
|
||||
public function getGateways()
|
||||
{
|
||||
$r = $this->powerboard->gatewayRequest('/v1/gateways', (\App\Enum\HttpVerb::GET)->value, [], []);
|
||||
|
||||
if($r->failed())
|
||||
$r->throw();
|
||||
|
||||
nlog($r->object());
|
||||
|
||||
return (new \App\PaymentDrivers\CBAPowerBoard\Models\Parse())->encode(Gateway::class."[]", $r->object()->resource->data);
|
||||
|
||||
}
|
||||
|
||||
/** We will need to have a process that updates this at intervals */
|
||||
public function updateSettings():self
|
||||
{
|
||||
$gateways = $this->getGateways();
|
||||
|
||||
$settings = $this->powerboard->company_gateway->getSettings();
|
||||
$settings->gateways = $gateways;
|
||||
$this->powerboard->company_gateway->setSettings($settings);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSettings(): mixed
|
||||
{
|
||||
return $this->powerboard->company_gateway->getSettings();
|
||||
}
|
||||
|
||||
public function getPaymentGatewayConfiguration(int $gateway_type_id): mixed
|
||||
{
|
||||
$type = self::GATEWAY_CBA;
|
||||
|
||||
match($gateway_type_id){
|
||||
\App\Models\GatewayType::CREDIT_CARD => $type = self::GATEWAY_CBA,
|
||||
default => $type = self::GATEWAY_CBA,
|
||||
};
|
||||
|
||||
return $this->getGatewayByType($type);
|
||||
}
|
||||
|
||||
private function getGatewayByType(string $gateway_type_const): mixed
|
||||
{
|
||||
$settings = $this->getSettings();
|
||||
|
||||
if(!property_exists($settings,'gateways')){
|
||||
$this->updateSettings();
|
||||
$settings = $this->getSettings();
|
||||
}
|
||||
|
||||
$gateways = (new \App\PaymentDrivers\CBAPowerBoard\Models\Parse())->encode(Gateway::class."[]", $settings->gateways);
|
||||
|
||||
return collect($gateways)->first(function (Gateway $gateway) use ($gateway_type_const){
|
||||
return $gateway->type == $gateway_type_const;
|
||||
});
|
||||
}
|
||||
|
||||
public function getGatewayId(int $gateway_type_id): string
|
||||
{
|
||||
$gateway = $this->getPaymentGatewayConfiguration($gateway_type_id);
|
||||
|
||||
return $gateway->_id;
|
||||
}
|
||||
}
|
222
app/PaymentDrivers/CBAPowerBoardPaymentDriver.php
Normal file
222
app/PaymentDrivers/CBAPowerBoardPaymentDriver.php
Normal file
@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\SystemLog;
|
||||
use App\Utils\HtmlEngine;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\PaymentType;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use App\PaymentDrivers\CBAPowerBoard\CreditCard;
|
||||
use App\PaymentDrivers\CBAPowerBoard\Customer;
|
||||
use App\PaymentDrivers\CBAPowerBoard\Settings;
|
||||
|
||||
/**
|
||||
* Class CBAPowerBoardPaymentDriver.
|
||||
*/
|
||||
class CBAPowerBoardPaymentDriver extends BaseDriver
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public $token_billing = true;
|
||||
|
||||
public $can_authorise_credit_card = false;
|
||||
|
||||
public $refundable = true;
|
||||
|
||||
public string $api_endpoint = 'https://api.powerboard.commbank.com.au';
|
||||
|
||||
public string $widget_endpoint = 'https://widget.powerboard.commbank.com.au/sdk/latest/widget.umd.min.js';
|
||||
|
||||
public string $environment = 'production_cba';
|
||||
|
||||
public const SYSTEM_LOG_TYPE = SystemLog::TYPE_POWERBOARD;
|
||||
|
||||
public static $methods = [
|
||||
GatewayType::CREDIT_CARD => CreditCard::class,
|
||||
];
|
||||
/**
|
||||
* Returns the gateway types.
|
||||
*/
|
||||
public function gatewayTypes(): array
|
||||
{
|
||||
|
||||
$types = [];
|
||||
|
||||
if ($this->client
|
||||
&& isset($this->client->country)
|
||||
&& in_array($this->client->country->iso_3166_3, ['AUS'])
|
||||
&& in_array($this->client->currency()->code, ['AUD'])
|
||||
) {
|
||||
$types[] = GatewayType::CREDIT_CARD;
|
||||
}
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
public function init(): self
|
||||
{
|
||||
if($this->company_gateway->getConfigField('testMode')) {
|
||||
$this->widget_endpoint = 'https://widget.preproduction.powerboard.commbank.com.au/sdk/latest/widget.umd.min.js';
|
||||
$this->api_endpoint = 'https://api.preproduction.powerboard.commbank.com.au';
|
||||
$this->environment = 'preproduction_cba';
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setPaymentMethod($payment_method_id)
|
||||
{
|
||||
|
||||
$class = self::$methods[$payment_method_id];
|
||||
|
||||
$this->payment_method = new $class($this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy method to pass the data into payment method authorizeView().
|
||||
*
|
||||
* @param array $data
|
||||
* @return \Illuminate\Http\RedirectResponse|mixed
|
||||
*/
|
||||
public function authorizeView(array $data)
|
||||
{
|
||||
$this->init();
|
||||
|
||||
return $this->payment_method->authorizeView($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the gateway response for credit card authorization.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|mixed
|
||||
*/
|
||||
public function authorizeResponse($request)
|
||||
{
|
||||
return $this->payment_method->authorizeResponse($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* View for displaying custom content of the driver.
|
||||
*
|
||||
* @param array $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function processPaymentView($data)
|
||||
{
|
||||
$this->init();
|
||||
|
||||
return $this->payment_method->paymentView($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processing method for payment. Should never be reached with this driver.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function processPaymentResponse($request)
|
||||
{
|
||||
return $this->payment_method->paymentResponse($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach payment method from custom payment driver.
|
||||
*
|
||||
* @param ClientGatewayToken $token
|
||||
* @return bool
|
||||
*/
|
||||
public function detach(ClientGatewayToken $token): bool
|
||||
{
|
||||
// Driver doesn't support this feature.
|
||||
return true;
|
||||
}
|
||||
|
||||
public function refund(Payment $payment, $amount, $return_client_response = false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function processWebhookRequest($request)
|
||||
{
|
||||
}
|
||||
|
||||
public function getClientRequiredFields(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
|
||||
{
|
||||
}
|
||||
|
||||
public function importCustomers()
|
||||
{
|
||||
}
|
||||
|
||||
public function auth(): bool
|
||||
{
|
||||
$this->init();
|
||||
|
||||
|
||||
return true;
|
||||
// try {
|
||||
// $this->verifyConnect();
|
||||
// return true;
|
||||
// } catch(\Exception $e) {
|
||||
|
||||
// }
|
||||
|
||||
// return false;
|
||||
|
||||
}
|
||||
|
||||
public function gatewayRequest(string $uri, string $verb, array $payload, array $headers = [])
|
||||
{
|
||||
$this->init();
|
||||
|
||||
$r = Http::withHeaders($this->getHeaders($headers))
|
||||
->{$verb}($this->api_endpoint.$uri, $payload);
|
||||
|
||||
nlog($r->body());
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
public function getHeaders(array $headers = []): array
|
||||
{
|
||||
return array_merge([
|
||||
'x-user-secret-key' => $this->company_gateway->getConfigField('secretKey'),
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
$headers);
|
||||
}
|
||||
|
||||
public function customer(): Customer
|
||||
{
|
||||
return new Customer($this);
|
||||
}
|
||||
|
||||
public function settings(): Settings
|
||||
{
|
||||
return new Settings($this);
|
||||
}
|
||||
}
|
50
database/migrations/2024_09_06_042040_cba_powerboard.php
Normal file
50
database/migrations/2024_09_06_042040_cba_powerboard.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Gateway;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
|
||||
Model::unguard();
|
||||
|
||||
$fields = new \stdClass();
|
||||
|
||||
$fields->publicKey = '';
|
||||
$fields->secretKey = '';
|
||||
$fields->testMode = false;
|
||||
$fields->threeds = false;
|
||||
|
||||
$powerboard = new Gateway();
|
||||
$powerboard->id = 64;
|
||||
$powerboard->name = 'CBA PowerBoard';
|
||||
$powerboard->provider = 'CBAPowerBoard';
|
||||
$powerboard->key = 'b67581d804dbad1743b61c57285142ad';
|
||||
$powerboard->sort_order = 4543;
|
||||
$powerboard->is_offsite = false;
|
||||
$powerboard->visible = true;
|
||||
$powerboard->fields = json_encode($fields);
|
||||
$powerboard->save();
|
||||
|
||||
|
||||
Schema::table("company_gateways", function (\Illuminate\Database\Schema\Blueprint $table){
|
||||
$table->text('settings')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
};
|
@ -88,7 +88,8 @@ class PaymentLibrariesSeeder extends Seeder
|
||||
['id' => 60, 'name' => 'PayPal REST', 'provider' => 'PayPal_Rest', 'key' => '80af24a6a691230bbec33e930ab40665', 'fields' => '{"clientId":"","secret":"","signature":"","testMode":false}'],
|
||||
['id' => 61, 'name' => 'PayPal Platform', 'provider' => 'PayPal_PPCP', 'key' => '80af24a6a691230bbec33e930ab40666', 'fields' => '{"testMode":false}'],
|
||||
['id' => 62, 'name' => 'BTCPay', 'provider' => 'BTCPay', 'key' => 'vpyfbmdrkqcicpkjqdusgjfluebftuva', 'fields' => '{"btcpayUrl":"", "apiKey":"", "storeId":"", "webhookSecret":""}'],
|
||||
['id' => 63, 'name' => 'Rotessa', 'is_offsite' => false, 'sort_order' => 22, 'provider' => 'Rotessa', 'key' => '91be24c7b792230bced33e930ac61676', 'fields' => '{"apiKey":"", "testMode":""}'],
|
||||
['id' => 63, 'name' => 'Rotessa', 'is_offsite' => false, 'sort_order' => 22, 'provider' => 'Rotessa', 'key' => '91be24c7b792230bced33e930ac61676', 'fields' => '{"apiKey":"", "testMode":false}'],
|
||||
['id' => 64, 'name' => 'CBA PowerBoard', 'is_offsite' => false, 'sort_order' => 26, 'provider' => 'CBAPowerBoard', 'key' => 'b67581d804dbad1743b61c57285142ad', 'fields' => '{"publicKey":"", "secretKey":"", "testMode":false, "Threeds":true}'],
|
||||
];
|
||||
|
||||
foreach ($gateways as $gateway) {
|
||||
|
@ -93,7 +93,7 @@ inset: 6px;
|
||||
document.getElementById("gateway_response").value =JSON.stringify( data );
|
||||
|
||||
formData = JSON.stringify(Object.fromEntries(new FormData(document.getElementById("server_response")))),
|
||||
|
||||
|
||||
fetch('{{ route('client.payments.response') }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
@ -0,0 +1,244 @@
|
||||
@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'Credit card', 'card_title' => 'Credit card'])
|
||||
|
||||
@section('gateway_head')
|
||||
<meta name="instant-payment" content="yes" />
|
||||
@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="gateway_response">
|
||||
<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="browser_details">
|
||||
<input type="hidden" name="charge">
|
||||
<button type="submit" class="hidden" id="stub">Submit</button>
|
||||
</form>
|
||||
|
||||
<div class="alert alert-failure mb-4" hidden id="errors"></div>
|
||||
|
||||
<div id="powerboard-payment-container" class="w-full">
|
||||
<div id="widget" style="block"></div>
|
||||
<div id="widget-3dsecure"></div>
|
||||
</div>
|
||||
|
||||
@component('portal.ninja2020.gateways.includes.pay_now', ['id' => 'authorize-card'])
|
||||
{{ ctrans('texts.add_payment_method') }}
|
||||
@endcomponent
|
||||
|
||||
@endsection
|
||||
|
||||
@section('gateway_footer')
|
||||
|
||||
<style>
|
||||
iframe {
|
||||
border: 0;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="{{ $widget_endpoint }}"></script>
|
||||
|
||||
<script>
|
||||
var widget = new cba.HtmlWidget('#widget', '{{ $public_key }}', '{{ $gateway_id }}');
|
||||
widget.setEnv("{{ $environment }}");
|
||||
widget.useAutoResize();
|
||||
// widget.interceptSubmitForm('#server-response');
|
||||
widget.onFinishInsert('input[name="gateway_response"]', "payment_source");
|
||||
widget.load();
|
||||
|
||||
widget.trigger('tab', function (data){
|
||||
|
||||
console.log("tab Response", data);
|
||||
|
||||
console.log(widget.isValidForm());
|
||||
|
||||
let payNow = document.getElementById('pay-now');
|
||||
|
||||
payNow.disabled = widget.isInvalidForm();
|
||||
|
||||
});
|
||||
|
||||
widget.trigger('submit_form',function (data){
|
||||
|
||||
console.log("submit_form Response", data);
|
||||
|
||||
console.log(widget.isValidForm());
|
||||
|
||||
let payNow = document.getElementById('pay-now');
|
||||
|
||||
payNow.disabled = widget.isInvalidForm();
|
||||
|
||||
});
|
||||
|
||||
widget.trigger('tab',function (data){
|
||||
|
||||
console.log("tab Response", data);
|
||||
|
||||
console.log(widget.isValidForm());
|
||||
|
||||
let payNow = document.getElementById('pay-now');
|
||||
|
||||
payNow.disabled = widget.isInvalidForm();
|
||||
|
||||
});
|
||||
|
||||
widget.on("systemError", function(data) {
|
||||
console.log("systemError Response", data);
|
||||
});
|
||||
|
||||
widget.on("validationError", function(data) {
|
||||
console.log("validationError", data);
|
||||
});
|
||||
|
||||
widget.on("finish", async function(data) {
|
||||
document.getElementById('errors').hidden = true;
|
||||
|
||||
console.log("finish", data);
|
||||
|
||||
try {
|
||||
const resource = await get3dsToken();
|
||||
console.log("3DS Token:", resource);
|
||||
|
||||
console.log("pre canvas");
|
||||
console.log(resource._3ds.token);
|
||||
|
||||
var canvas = new cba.Canvas3ds('#widget-3dsecure', resource._3ds.token);
|
||||
canvas.load();
|
||||
|
||||
let widget = document.getElementById('widget');
|
||||
widget.classList.add('hidden');
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error fetching 3DS Token:", error);
|
||||
}
|
||||
|
||||
canvas.on("chargeAuthSuccess", function(data) {
|
||||
console.log(data);
|
||||
|
||||
document.querySelector(
|
||||
'input[name="browser_details"]'
|
||||
).value = null;
|
||||
|
||||
document.querySelector(
|
||||
'input[name="charge"]'
|
||||
).value = JSON.stringify(data);
|
||||
|
||||
document.getElementById('server-response').submit();
|
||||
|
||||
});
|
||||
|
||||
canvas.on("chargeAuthReject", function(data) {
|
||||
console.log(data);
|
||||
|
||||
document.getElementById('errors').textContent = `Sorry, your transaction could not be processed...`;
|
||||
document.getElementById('errors').hidden = false;
|
||||
|
||||
});
|
||||
|
||||
canvas.load();
|
||||
|
||||
});
|
||||
|
||||
widget.on("submit", async function (data){
|
||||
console.log("submit");
|
||||
console.log(data);
|
||||
document.getElementById('errors').hidden = true;
|
||||
})
|
||||
|
||||
widget.on('form_submit', function (data) {
|
||||
console.log("form_submit", data);
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
widget.on('submit', function (data) {
|
||||
console.log("submit", data);
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
widget.on('tab', function (data) {
|
||||
console.log("tab", data);
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
let payNow = document.getElementById('authorize-card');
|
||||
|
||||
payNow.addEventListener('click', () => {
|
||||
|
||||
payNow.disabled = true;
|
||||
payNow.querySelector('svg').classList.remove('hidden');
|
||||
payNow.querySelector('span').classList.add('hidden');
|
||||
|
||||
document.getElementById('server-response').submit();
|
||||
|
||||
});
|
||||
|
||||
|
||||
async function get3dsToken() {
|
||||
|
||||
const browserDetails = {
|
||||
name: navigator.userAgent.substring(0, 100), // The full user agent string, which contains the browser name and version
|
||||
java_enabled: navigator.javaEnabled() ? "true" : "false", // Indicates if Java is enabled in the browser
|
||||
language: navigator.language || navigator.userLanguage, // The browser language
|
||||
screen_height: window.screen.height.toString(), // Screen height in pixels
|
||||
screen_width: window.screen.width.toString(), // Screen width in pixels
|
||||
time_zone: (new Date().getTimezoneOffset() * -1).toString(), // Timezone offset in minutes (negative for behind UTC)
|
||||
color_depth: window.screen.colorDepth.toString() // Color depth in bits per pixel
|
||||
};
|
||||
|
||||
document.querySelector(
|
||||
'input[name="browser_details"]'
|
||||
).value = JSON.stringify(browserDetails);
|
||||
|
||||
const formData = JSON.stringify(Object.fromEntries(new FormData(document.getElementById("server-response"))));
|
||||
|
||||
try {
|
||||
// Return the fetch promise to handle it externally
|
||||
const response = await fetch('{{ route('client.payment_methods.store', ['method' => App\Models\GatewayType::CREDIT_CARD]) }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
"Accept": 'application/json',
|
||||
"X-CSRF-Token": document.querySelector('meta[name="csrf-token"]').content
|
||||
},
|
||||
body: formData
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
|
||||
return await response.json().then(errorData => {
|
||||
throw new Error(errorData.message ?? 'Unknown error.');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return await response.json()
|
||||
|
||||
}
|
||||
catch(error) {
|
||||
|
||||
document.getElementById('errors').textContent = `Sorry, your card could not be authorized...\n\n${error.message}`;
|
||||
document.getElementById('errors').hidden = false;
|
||||
|
||||
console.error('Fetch error:', error); // Log error for debugging
|
||||
throw error; //
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const first = document.querySelector('input[name="payment-type"]');
|
||||
|
||||
if (first) {
|
||||
first.click();
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
||||
|
||||
|
@ -0,0 +1,140 @@
|
||||
@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'Credit card', 'card_title' => 'Credit card'])
|
||||
|
||||
@section('gateway_head')
|
||||
<meta name="instant-payment" content="yes" />
|
||||
@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="gateway_response">
|
||||
<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="charge_no3d">
|
||||
<button type="submit" class="hidden" id="stub">Submit</button>
|
||||
</form>
|
||||
|
||||
<div class="alert alert-failure mb-4" hidden id="errors"></div>
|
||||
|
||||
<div id="powerboard-payment-container" class="w-full">
|
||||
<div id="widget" style="block"></div>
|
||||
</div>
|
||||
|
||||
@component('portal.ninja2020.gateways.includes.pay_now', ['id' => 'authorize-card'])
|
||||
{{ ctrans('texts.add_payment_method') }}
|
||||
@endcomponent
|
||||
|
||||
@endsection
|
||||
|
||||
@section('gateway_footer')
|
||||
|
||||
<style>
|
||||
iframe {
|
||||
border: 0;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="{{ $widget_endpoint }}"></script>
|
||||
|
||||
<script>
|
||||
var widget = new cba.HtmlWidget('#widget', '{{ $public_key }}', '{{ $gateway_id }}');
|
||||
widget.setEnv("{{ $environment }}");
|
||||
widget.useAutoResize();
|
||||
widget.interceptSubmitForm('#server-response');
|
||||
widget.onFinishInsert('input[name="gateway_response"]', "payment_source");
|
||||
widget.load();
|
||||
|
||||
widget.trigger('tab', function (data){
|
||||
|
||||
console.log("tab Response", data);
|
||||
|
||||
console.log(widget.isValidForm());
|
||||
|
||||
let payNow = document.getElementById('pay-now');
|
||||
|
||||
payNow.disabled = widget.isInvalidForm();
|
||||
|
||||
});
|
||||
|
||||
widget.trigger('submit_form',function (data){
|
||||
|
||||
console.log("submit_form Response", data);
|
||||
|
||||
console.log(widget.isValidForm());
|
||||
|
||||
let payNow = document.getElementById('pay-now');
|
||||
|
||||
payNow.disabled = widget.isInvalidForm();
|
||||
|
||||
});
|
||||
|
||||
widget.trigger('tab',function (data){
|
||||
|
||||
console.log("tab Response", data);
|
||||
|
||||
console.log(widget.isValidForm());
|
||||
|
||||
let payNow = document.getElementById('pay-now');
|
||||
|
||||
payNow.disabled = widget.isInvalidForm();
|
||||
|
||||
});
|
||||
|
||||
widget.on("systemError", function(data) {
|
||||
console.log("systemError Response", data);
|
||||
});
|
||||
|
||||
widget.on("validationError", function(data) {
|
||||
console.log("validationError", data);
|
||||
});
|
||||
|
||||
widget.on("finish", async function(data) {
|
||||
document.getElementById('errors').hidden = true;
|
||||
console.log("finish", data);
|
||||
});
|
||||
|
||||
widget.on("submit", async function (data){
|
||||
console.log("submit");
|
||||
console.log(data);
|
||||
document.getElementById('errors').hidden = true;
|
||||
})
|
||||
|
||||
widget.on('form_submit', function (data) {
|
||||
console.log("form_submit", data);
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
widget.on('submit', function (data) {
|
||||
console.log("submit", data);
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
widget.on('tab', function (data) {
|
||||
console.log("tab", data);
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
let payNow = document.getElementById('authorize-card');
|
||||
|
||||
payNow.addEventListener('click', () => {
|
||||
|
||||
document.querySelector(
|
||||
'input[name="charge_no3d"]'
|
||||
).value = true;
|
||||
payNow.disabled = true;
|
||||
payNow.querySelector('svg').classList.remove('hidden');
|
||||
payNow.querySelector('span').classList.add('hidden');
|
||||
|
||||
document.getElementById('stub').click();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
||||
|
||||
|
@ -0,0 +1,337 @@
|
||||
@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'Credit card', 'card_title' => 'Credit card'])
|
||||
|
||||
@section('gateway_head')
|
||||
<meta name="instant-payment" content="yes" />
|
||||
@endsection
|
||||
|
||||
@section('gateway_content')
|
||||
|
||||
<form action="javascript:void(0);" id="stepone">
|
||||
<input type="hidden" name="gateway_response">
|
||||
<button type="submit" class="hidden" id="stepone_submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<form action="{{ route('client.payments.response') }}" method="post" id="server-response">
|
||||
@csrf
|
||||
<input type="hidden" name="gateway_response">
|
||||
<input type="hidden" name="store_card" id="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">
|
||||
<input type="hidden" name="browser_details">
|
||||
<input type="hidden" name="charge">
|
||||
<button type="submit" class="hidden" id="stub">Submit</button>
|
||||
</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')])
|
||||
<ul class="list-none">
|
||||
@if(count($tokens) > 0)
|
||||
@foreach($tokens as $token)
|
||||
<li class="py-2 cursor-pointer">
|
||||
<label class="mr-4">
|
||||
<input
|
||||
type="radio"
|
||||
data-token="{{ $token->token }}"
|
||||
name="payment-type"
|
||||
class="form-check-input text-indigo-600 rounded-full cursor-pointer toggle-payment-with-token toggle-payment-with-token"/>
|
||||
<span class="ml-1 cursor-pointer">**** {{ $token->meta?->last4 }}</span>
|
||||
</label>
|
||||
</li>
|
||||
@endforeach
|
||||
@endisset
|
||||
|
||||
<li class="py-2 cursor-pointer">
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
id="toggle-payment-with-credit-card"
|
||||
class="form-check-input text-indigo-600 rounded-full cursor-pointer"
|
||||
name="payment-type"
|
||||
checked/>
|
||||
<span class="ml-1 cursor-pointer">{{ __('texts.new_card') }}</span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
@endcomponent
|
||||
|
||||
<div id="powerboard-payment-container" class="w-full">
|
||||
<div id="widget" style="block" class="hidden"></div>
|
||||
<div id="widget-3dsecure"></div>
|
||||
</div>
|
||||
|
||||
@include('portal.ninja2020.gateways.includes.save_card')
|
||||
@include('portal.ninja2020.gateways.includes.pay_now')
|
||||
|
||||
@endsection
|
||||
|
||||
@section('gateway_footer')
|
||||
|
||||
<style>
|
||||
iframe {
|
||||
border: 0;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="{{ $widget_endpoint }}"></script>
|
||||
|
||||
<script>
|
||||
|
||||
/** Loads the widget, links the widget to a dummy form */
|
||||
var widget = new cba.HtmlWidget('#widget', '{{ $public_key }}', '{{ $gateway_id }}');
|
||||
widget.setEnv("{{ $environment }}");
|
||||
widget.useAutoResize();
|
||||
widget.interceptSubmitForm('#stepone');
|
||||
widget.onFinishInsert('#server-response input[name="gateway_response"]', "payment_source");
|
||||
widget.load();
|
||||
|
||||
widget.on("systemError", function(data) {
|
||||
console.log("systemError Response", data);
|
||||
});
|
||||
|
||||
widget.on("validationError", function(data) {
|
||||
console.log("validationError", data);
|
||||
});
|
||||
|
||||
/** Retrieves the payment_source token and passes this to the 3ds handler */
|
||||
widget.on("finish", function(data) {
|
||||
document.getElementById('errors').hidden = true;
|
||||
|
||||
console.log("finish");
|
||||
console.log(data);
|
||||
|
||||
process3ds();
|
||||
|
||||
});
|
||||
|
||||
widget.on("submit", function (data){
|
||||
console.log("submit");
|
||||
console.log(data);
|
||||
document.getElementById('errors').hidden = true;
|
||||
})
|
||||
|
||||
widget.on('form_submit', function (data) {
|
||||
console.log("form_submit", data);
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
widget.on('tab', function (data) {
|
||||
console.log("tab", data);
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
let payNow = document.getElementById('pay-now');
|
||||
|
||||
payNow.addEventListener('click', () => {
|
||||
|
||||
|
||||
const div = document.getElementById('widget');
|
||||
|
||||
widget.getValidationState();
|
||||
|
||||
if(!widget.isValidForm() && div.offsetParent !== null){
|
||||
console.log("invalid");
|
||||
payNow.disabled = false;
|
||||
payNow.querySelector('svg').classList.add('hidden');
|
||||
payNow.querySelector('span').classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
payNow.disabled = true;
|
||||
payNow.querySelector('svg').classList.remove('hidden');
|
||||
payNow.querySelector('span').classList.add('hidden');
|
||||
|
||||
let storeCard = document.querySelector(
|
||||
'input[name=token-billing-checkbox]:checked'
|
||||
);
|
||||
|
||||
if (storeCard) {
|
||||
document.getElementById('store_card').value = storeCard.value;
|
||||
}
|
||||
|
||||
|
||||
if(div.offsetParent !== null)
|
||||
document.getElementById('stepone_submit').click();
|
||||
else
|
||||
document.getElementById('server-response').submit();
|
||||
|
||||
});
|
||||
|
||||
async function process3ds()
|
||||
{
|
||||
|
||||
|
||||
try {
|
||||
const resource = await get3dsToken();
|
||||
console.log("3DS Token:", resource);
|
||||
|
||||
console.log("pre canvas");
|
||||
console.log(resource._3ds.token);
|
||||
|
||||
if (resource.status == "not_authenticated" || resource.status == "authentication_not_supported") { // if status = authentication_not_supported, there will be no resource._3ds.token, so we need to fall back to a NON 3ds payment flow from here
|
||||
throw new Error('There was an issue authenticating this payment method.');
|
||||
return;
|
||||
}
|
||||
|
||||
var canvas = new cba.Canvas3ds('#widget-3dsecure', resource._3ds.token);
|
||||
canvas.load();
|
||||
|
||||
let widget = document.getElementById('widget');
|
||||
widget.classList.add('hidden');
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error fetching 3DS Token:", error);
|
||||
|
||||
document.getElementById('errors').textContent = `Sorry, your transaction could not be processed...\n\n${error}`;
|
||||
document.getElementById('errors').hidden = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
canvas.on("chargeAuthSuccess", function(data) {
|
||||
console.log(data);
|
||||
|
||||
document.querySelector(
|
||||
'input[name="browser_details"]'
|
||||
).value = null;
|
||||
|
||||
document.querySelector(
|
||||
'input[name="charge"]'
|
||||
).value = JSON.stringify(data);
|
||||
|
||||
let storeCard = document.querySelector(
|
||||
'input[name=token-billing-checkbox]:checked'
|
||||
);
|
||||
|
||||
if (storeCard) {
|
||||
document.getElementById('store_card').value = storeCard.value;
|
||||
}
|
||||
|
||||
document.getElementById('server-response').submit();
|
||||
|
||||
});
|
||||
|
||||
canvas.on("chargeAuthReject", function(data) {
|
||||
console.log(data);
|
||||
|
||||
document.getElementById('errors').textContent = `Sorry, your transaction could not be processed...`;
|
||||
document.getElementById('errors').hidden = false;
|
||||
|
||||
});
|
||||
|
||||
canvas.load();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
async function get3dsToken() {
|
||||
|
||||
const browserDetails = {
|
||||
name: navigator.userAgent.substring(0, 100), // The full user agent string, which contains the browser name and version
|
||||
java_enabled: navigator.javaEnabled() ? "true" : "false", // Indicates if Java is enabled in the browser
|
||||
language: navigator.language || navigator.userLanguage, // The browser language
|
||||
screen_height: window.screen.height.toString(), // Screen height in pixels
|
||||
screen_width: window.screen.width.toString(), // Screen width in pixels
|
||||
time_zone: (new Date().getTimezoneOffset() * -1).toString(), // Timezone offset in minutes (negative for behind UTC)
|
||||
color_depth: window.screen.colorDepth.toString() // Color depth in bits per pixel
|
||||
};
|
||||
|
||||
document.querySelector(
|
||||
'input[name="browser_details"]'
|
||||
).value = JSON.stringify(browserDetails);
|
||||
|
||||
const formData = JSON.stringify(Object.fromEntries(new FormData(document.getElementById("server-response"))));
|
||||
|
||||
try {
|
||||
// Return the fetch promise to handle it externally
|
||||
const response = await fetch('{{ route('client.payments.response') }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"X-Requested-With": "XMLHttpRequest",
|
||||
"Accept": 'application/json',
|
||||
"X-CSRF-Token": document.querySelector('meta[name="csrf-token"]').content
|
||||
},
|
||||
body: formData
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
|
||||
return await response.json().then(errorData => {
|
||||
throw new Error(errorData.message ?? 'Unknown error.');
|
||||
});
|
||||
|
||||
// const text = await response.text();
|
||||
// throw new Error(`Network response was not ok: ${response.statusText}. Response text: ${text}`);
|
||||
}
|
||||
|
||||
return await response.json()
|
||||
|
||||
}
|
||||
catch(error) {
|
||||
|
||||
document.getElementById('errors').textContent = `Sorry, your transaction could not be processed...\n\n${error.message}`;
|
||||
document.getElementById('errors').hidden = false;
|
||||
|
||||
console.error('Fetch error:', error); // Log error for debugging
|
||||
throw error; //
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
const first = document.querySelector('input[name="payment-type"]');
|
||||
|
||||
if (first) {
|
||||
first.click();
|
||||
}
|
||||
|
||||
|
||||
document
|
||||
.getElementById('toggle-payment-with-credit-card')
|
||||
.addEventListener('click', (element) => {
|
||||
|
||||
let widget = document.getElementById('widget');
|
||||
widget.classList.remove('hidden');
|
||||
document.getElementById('save-card--container').style.display ='grid';
|
||||
document.querySelector('input[name=token]').value = '';
|
||||
|
||||
});
|
||||
|
||||
Array.from(
|
||||
document.getElementsByClassName('toggle-payment-with-token')
|
||||
).forEach((element) =>
|
||||
element.addEventListener('click', (element) => {
|
||||
document
|
||||
.getElementById('widget')
|
||||
.classList.add('hidden');
|
||||
document.getElementById(
|
||||
'save-card--container'
|
||||
).style.display = 'none';
|
||||
document.querySelector('input[name=token]').value =
|
||||
element.target.dataset.token;
|
||||
})
|
||||
);
|
||||
</script>
|
||||
|
||||
@endsection
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user