diff --git a/app/Helpers/Sanitizer.php b/app/Helpers/Sanitizer.php new file mode 100644 index 000000000000..2c8598b00859 --- /dev/null +++ b/app/Helpers/Sanitizer.php @@ -0,0 +1,28 @@ +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) { diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index ce561f31bf0c..73dbae6617d7 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -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]); diff --git a/app/Models/Gateway.php b/app/Models/Gateway.php index 1bbfe1b95fae..d1276d931348 100644 --- a/app/Models/Gateway.php +++ b/app/Models/Gateway.php @@ -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 []; } diff --git a/app/Models/SystemLog.php b/app/Models/SystemLog.php index a24947a0f6d3..acc05c3246db 100644 --- a/app/Models/SystemLog.php +++ b/app/Models/SystemLog.php @@ -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; diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php index ff89084efc6e..23f7a81545f9 100644 --- a/app/PaymentDrivers/BaseDriver.php +++ b/app/PaymentDrivers/BaseDriver.php @@ -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', [ diff --git a/app/PaymentDrivers/CBAPowerBoard/CreditCard.php b/app/PaymentDrivers/CBAPowerBoard/CreditCard.php new file mode 100644 index 000000000000..859020e8cdfd --- /dev/null +++ b/app/PaymentDrivers/CBAPowerBoard/CreditCard.php @@ -0,0 +1,465 @@ +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]); + + } + +} diff --git a/app/PaymentDrivers/CBAPowerBoard/Customer.php b/app/PaymentDrivers/CBAPowerBoard/Customer.php new file mode 100644 index 000000000000..9ac9e75da6c1 --- /dev/null +++ b/app/PaymentDrivers/CBAPowerBoard/Customer.php @@ -0,0 +1,186 @@ +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(); + } + +} + diff --git a/app/PaymentDrivers/CBAPowerBoard/Models/Charge.php b/app/PaymentDrivers/CBAPowerBoard/Models/Charge.php new file mode 100644 index 000000000000..a165876c0608 --- /dev/null +++ b/app/PaymentDrivers/CBAPowerBoard/Models/Charge.php @@ -0,0 +1,84 @@ +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; + } +} diff --git a/app/PaymentDrivers/CBAPowerBoard/Models/Customer.php b/app/PaymentDrivers/CBAPowerBoard/Models/Customer.php new file mode 100644 index 000000000000..a207b79ec719 --- /dev/null +++ b/app/PaymentDrivers/CBAPowerBoard/Models/Customer.php @@ -0,0 +1,85 @@ +_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; + } +} \ No newline at end of file diff --git a/app/PaymentDrivers/CBAPowerBoard/Models/Gateway.php b/app/PaymentDrivers/CBAPowerBoard/Models/Gateway.php new file mode 100644 index 000000000000..d36e176d5ffb --- /dev/null +++ b/app/PaymentDrivers/CBAPowerBoard/Models/Gateway.php @@ -0,0 +1,56 @@ +_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; + } +} diff --git a/app/PaymentDrivers/CBAPowerBoard/Models/Parse.php b/app/PaymentDrivers/CBAPowerBoard/Models/Parse.php new file mode 100644 index 000000000000..55d213a33e3e --- /dev/null +++ b/app/PaymentDrivers/CBAPowerBoard/Models/Parse.php @@ -0,0 +1,70 @@ +deserialize(json_encode($document), $object_type, 'json', [\Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer::SKIP_NULL_VALUES => true]); + + return $data; + + } +} \ No newline at end of file diff --git a/app/PaymentDrivers/CBAPowerBoard/Models/PaymentSource.php b/app/PaymentDrivers/CBAPowerBoard/Models/PaymentSource.php new file mode 100644 index 000000000000..019b2fcfa9d0 --- /dev/null +++ b/app/PaymentDrivers/CBAPowerBoard/Models/PaymentSource.php @@ -0,0 +1,96 @@ +_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; + } +} diff --git a/app/PaymentDrivers/CBAPowerBoard/Models/PaymentSources.php b/app/PaymentDrivers/CBAPowerBoard/Models/PaymentSources.php new file mode 100644 index 000000000000..0698e6f4078a --- /dev/null +++ b/app/PaymentDrivers/CBAPowerBoard/Models/PaymentSources.php @@ -0,0 +1,18 @@ +_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; + } +} diff --git a/app/PaymentDrivers/CBAPowerBoard/Settings.php b/app/PaymentDrivers/CBAPowerBoard/Settings.php new file mode 100644 index 000000000000..945a72f48ea7 --- /dev/null +++ b/app/PaymentDrivers/CBAPowerBoard/Settings.php @@ -0,0 +1,96 @@ +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; + } +} diff --git a/app/PaymentDrivers/CBAPowerBoardPaymentDriver.php b/app/PaymentDrivers/CBAPowerBoardPaymentDriver.php new file mode 100644 index 000000000000..5c9b6055e998 --- /dev/null +++ b/app/PaymentDrivers/CBAPowerBoardPaymentDriver.php @@ -0,0 +1,222 @@ + 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); + } +} \ No newline at end of file diff --git a/database/migrations/2024_09_06_042040_cba_powerboard.php b/database/migrations/2024_09_06_042040_cba_powerboard.php new file mode 100644 index 000000000000..7c993e310d54 --- /dev/null +++ b/database/migrations/2024_09_06_042040_cba_powerboard.php @@ -0,0 +1,50 @@ +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 + { + // + } +}; diff --git a/database/seeders/PaymentLibrariesSeeder.php b/database/seeders/PaymentLibrariesSeeder.php index 49c49e87b44e..879b65dc8831 100644 --- a/database/seeders/PaymentLibrariesSeeder.php +++ b/database/seeders/PaymentLibrariesSeeder.php @@ -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) { diff --git a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php index b6c83d9149e9..cac76ec1a7ce 100644 --- a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php @@ -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: { diff --git a/resources/views/portal/ninja2020/gateways/powerboard/credit_card/authorize.blade.php b/resources/views/portal/ninja2020/gateways/powerboard/credit_card/authorize.blade.php new file mode 100644 index 000000000000..e13ccff69d71 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/powerboard/credit_card/authorize.blade.php @@ -0,0 +1,244 @@ +@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'Credit card', 'card_title' => 'Credit card']) + +@section('gateway_head') + +@endsection + +@section('gateway_content') + +
+ @csrf + + + + + + +
+ + + +
+
+
+
+ + @component('portal.ninja2020.gateways.includes.pay_now', ['id' => 'authorize-card']) + {{ ctrans('texts.add_payment_method') }} + @endcomponent + +@endsection + +@section('gateway_footer') + + + + + + + +@endsection + + + diff --git a/resources/views/portal/ninja2020/gateways/powerboard/credit_card/authorize_no_3ds.blade.php b/resources/views/portal/ninja2020/gateways/powerboard/credit_card/authorize_no_3ds.blade.php new file mode 100644 index 000000000000..3c8c6b6dd038 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/powerboard/credit_card/authorize_no_3ds.blade.php @@ -0,0 +1,140 @@ +@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'Credit card', 'card_title' => 'Credit card']) + +@section('gateway_head') + +@endsection + +@section('gateway_content') + +
+ @csrf + + + + + +
+ + + +
+
+
+ + @component('portal.ninja2020.gateways.includes.pay_now', ['id' => 'authorize-card']) + {{ ctrans('texts.add_payment_method') }} + @endcomponent + +@endsection + +@section('gateway_footer') + + + + + + + +@endsection + + + diff --git a/resources/views/portal/ninja2020/gateways/powerboard/credit_card/pay.blade.php b/resources/views/portal/ninja2020/gateways/powerboard/credit_card/pay.blade.php new file mode 100644 index 000000000000..b656b2f04245 --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/powerboard/credit_card/pay.blade.php @@ -0,0 +1,337 @@ +@extends('portal.ninja2020.layout.payments', ['gateway_title' => 'Credit card', 'card_title' => 'Credit card']) + +@section('gateway_head') + +@endsection + +@section('gateway_content') + +
+ + +
+ +
+ @csrf + + + + + + + + + + + +
+ + + + @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')]) + + @endcomponent + +
+ +
+
+ + @include('portal.ninja2020.gateways.includes.save_card') + @include('portal.ninja2020.gateways.includes.pay_now') + +@endsection + +@section('gateway_footer') + + + + + + + +@endsection + + +