diff --git a/app/Console/Commands/CreateSingleAccount.php b/app/Console/Commands/CreateSingleAccount.php index 7dde1e3bc91d..df64204634eb 100644 --- a/app/Console/Commands/CreateSingleAccount.php +++ b/app/Console/Commands/CreateSingleAccount.php @@ -25,6 +25,7 @@ use App\Models\Account; use App\Models\Company; use App\Models\Country; use App\Models\Expense; +use App\Models\Gateway; use App\Models\Invoice; use App\Models\Product; use App\Models\Project; diff --git a/app/Http/Controllers/CompanyGatewayController.php b/app/Http/Controllers/CompanyGatewayController.php index 9914327f526c..990a886f906b 100644 --- a/app/Http/Controllers/CompanyGatewayController.php +++ b/app/Http/Controllers/CompanyGatewayController.php @@ -194,21 +194,27 @@ class CompanyGatewayController extends BaseController */ public function store(StoreCompanyGatewayRequest $request) { - $company_gateway = CompanyGatewayFactory::create(auth()->user()->company()->id, auth()->user()->id); + /** @var \App\Models\User $user */ + $user = auth()->user(); + + $company_gateway = CompanyGatewayFactory::create($user->company()->id, $user->id); $company_gateway->fill($request->all()); $company_gateway->save(); - /*Always ensure at least one fees and limits object is set per gateway*/ - if (! isset($company_gateway->fees_and_limits)) { - $gateway_types = $company_gateway->driver(new Client)->gatewayTypes(); - - $fees_and_limits = new \stdClass; - $fees_and_limits->{$gateway_types[0]} = new FeesAndLimits; - - $company_gateway->fees_and_limits = $fees_and_limits; - $company_gateway->save(); + /*Always ensure at least one fees and limits object is set per gateway*/ + $gateway_types = $company_gateway->driver(new Client)->getAvailableMethods(); + + $fees_and_limits = $company_gateway->fees_and_limits; + + foreach($gateway_types as $key => $gateway_type) + { + if(!property_exists($fees_and_limits, $key)) + $fees_and_limits->{$key} = new FeesAndLimits; } + $company_gateway->fees_and_limits = $fees_and_limits; + $company_gateway->save(); + ApplePayDomain::dispatch($company_gateway, $company_gateway->company->db); if (in_array($company_gateway->gateway_key, $this->stripe_keys)) { @@ -381,10 +387,18 @@ class CompanyGatewayController extends BaseController { $company_gateway->fill($request->all()); - if (! $request->has('fees_and_limits')) { - $company_gateway->fees_and_limits = ''; + /*Always ensure at least one fees and limits object is set per gateway*/ + $gateway_types = $company_gateway->driver(new Client)->getAvailableMethods(); + + $fees_and_limits = $company_gateway->fees_and_limits; + + foreach($gateway_types as $key => $gateway_type) { + if(!property_exists($fees_and_limits, $key)) { + $fees_and_limits->{$key} = new FeesAndLimits; + } } + $company_gateway->fees_and_limits = $fees_and_limits; $company_gateway->save(); if($company_gateway->gateway_key == $this->checkout_key) { diff --git a/app/Http/Controllers/Reports/VendorReportController.php b/app/Http/Controllers/Reports/VendorReportController.php index d86f031df741..4ee3e935442f 100644 --- a/app/Http/Controllers/Reports/VendorReportController.php +++ b/app/Http/Controllers/Reports/VendorReportController.php @@ -42,7 +42,11 @@ class VendorReportController extends BaseController $hash = \Illuminate\Support\Str::uuid(); +<<<<<<< HEAD PreviewReport::dispatch($user->company(), $request->all(), VendorExport::class, $hash); +======= + $hash = \Illuminate\Support\Str::uuid()->toString(); +>>>>>>> paypal_ppcp return response()->json(['message' => $hash], 200); diff --git a/app/Http/Requests/CompanyGateway/StoreCompanyGatewayRequest.php b/app/Http/Requests/CompanyGateway/StoreCompanyGatewayRequest.php index 5f148f504354..d007de24275f 100644 --- a/app/Http/Requests/CompanyGateway/StoreCompanyGatewayRequest.php +++ b/app/Http/Requests/CompanyGateway/StoreCompanyGatewayRequest.php @@ -11,6 +11,7 @@ namespace App\Http\Requests\CompanyGateway; +use App\DataMapper\FeesAndLimits; use App\Http\Requests\Request; use App\Http\ValidationRules\ValidCompanyGatewayFeesAndLimitsRule; use App\Models\Gateway; @@ -28,7 +29,10 @@ class StoreCompanyGatewayRequest extends Request */ public function authorize() : bool { - return auth()->user()->isAdmin(); + /** @var \App\Models\User $user */ + $user = auth()->user(); + + return $user->isAdmin(); } public function rules() @@ -64,6 +68,7 @@ class StoreCompanyGatewayRequest extends Request if (isset($input['fees_and_limits'])) { $input['fees_and_limits'] = $this->cleanFeesAndLimits($input['fees_and_limits']); } + } $this->replace($input); diff --git a/app/Http/Requests/CompanyGateway/UpdateCompanyGatewayRequest.php b/app/Http/Requests/CompanyGateway/UpdateCompanyGatewayRequest.php index 465449cce28a..3949bdb80404 100644 --- a/app/Http/Requests/CompanyGateway/UpdateCompanyGatewayRequest.php +++ b/app/Http/Requests/CompanyGateway/UpdateCompanyGatewayRequest.php @@ -27,7 +27,10 @@ class UpdateCompanyGatewayRequest extends Request */ public function authorize() { - return auth()->user()->isAdmin(); + /** @var \App\Models\User $user */ + $user = auth()->user(); + + return $user->isAdmin(); } public function rules() diff --git a/app/Listeners/LogRequestSending.php b/app/Listeners/LogRequestSending.php new file mode 100644 index 000000000000..c123ef809216 --- /dev/null +++ b/app/Listeners/LogRequestSending.php @@ -0,0 +1,43 @@ +request->headers()); + nlog($event->request->url()); + nlog(json_encode($event->request->headers())); + nlog($event->request->body()); + + } +} diff --git a/app/Listeners/LogResponseReceived.php b/app/Listeners/LogResponseReceived.php new file mode 100644 index 000000000000..77dff2029936 --- /dev/null +++ b/app/Listeners/LogResponseReceived.php @@ -0,0 +1,56 @@ +request->headers()); + nlog($event->request->url()); + nlog(json_encode($event->request->headers())); + nlog($event->request->body()); + + nlog("Response"); + nlog($event->response->headers()); + nlog(json_encode($event->response->headers())); + nlog($event->response->body()); + nlog($event->response->json()); + } +} diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index ccb25724ef9f..21b61b9c3293 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -117,7 +117,6 @@ class CompanyGateway extends BaseModel 16 => ['card' => 'images/credit_cards/Test-Discover-Icon.png', 'text' => 'Discover'], ]; - // const TYPE_PAYPAL = 300; // const TYPE_STRIPE = 301; // const TYPE_LEDGER = 302; @@ -132,6 +131,7 @@ class CompanyGateway extends BaseModel // const TYPE_MOLLIE = 312; // const TYPE_EWAY = 313; // const TYPE_FORTE = 314; + // const PAYPAL_PPCP = 323; public $gateway_consts = [ '38f2c48af60c7dd69e04248cbb24c36e' => 300, @@ -150,6 +150,7 @@ class CompanyGateway extends BaseModel '65faab2ab6e3223dbe848b1686490baz' => 320, 'b9886f9257f0c6ee7c302f1c74475f6c' => 321, 'hxd6gwg3ekb9tb3v9lptgx1mqyg69zu9' => 322, + '80af24a6a691230bbec33e930ab40666' => 323, ]; protected $touches = []; diff --git a/app/Models/Gateway.php b/app/Models/Gateway.php index 681889a68e88..d811ea7ed064 100644 --- a/app/Models/Gateway.php +++ b/app/Models/Gateway.php @@ -90,7 +90,7 @@ class Gateway extends StaticModel if ($this->id == 1) { $link = 'http://reseller.authorize.net/application/?id=5560364'; - } elseif ($this->id == 15) { + } elseif (in_array($this->id,[15,60,61])) { $link = 'https://www.paypal.com/us/cgi-bin/webscr?cmd=_login-api-run'; } elseif ($this->id == 24) { $link = 'https://www.2checkout.com/referral?r=2c37ac2298'; @@ -202,7 +202,19 @@ class Gateway extends StaticModel // GatewayType::PRZELEWY24 => ['refund' => false, 'token_billing' => false], // GatewayType::SOFORT => ['refund' => false, 'token_billing' => false], ]; //Paypal - + case 61: + return [ + GatewayType::PAYPAL => ['refund' => false, 'token_billing' => false], + GatewayType::CREDIT_CARD => ['refund' => false, 'token_billing' => false], + GatewayType::VENMO => ['refund' => false, 'token_billing' => false], + // GatewayType::SEPA => ['refund' => false, 'token_billing' => false], + // GatewayType::BANCONTACT => ['refund' => false, 'token_billing' => false], + // GatewayType::EPS => ['refund' => false, 'token_billing' => false], + // GatewayType::MYBANK => ['refund' => false, 'token_billing' => false], + // GatewayType::PAYLATER => ['refund' => false, 'token_billing' => false], + // GatewayType::PRZELEWY24 => ['refund' => false, 'token_billing' => false], + // GatewayType::SOFORT => ['refund' => false, 'token_billing' => false], + ]; //Paypal PPCP default: return []; } diff --git a/app/Models/Presenters/ClientPresenter.php b/app/Models/Presenters/ClientPresenter.php index e9ada6d652ed..bd3a322e3373 100644 --- a/app/Models/Presenters/ClientPresenter.php +++ b/app/Models/Presenters/ClientPresenter.php @@ -110,6 +110,11 @@ class ClientPresenter extends EntityPresenter return $str; } + public function shipping_country_code(): string + { + return $this->entity->shipping_country ? $this->entity->shipping_country->iso_3166_2 : $this->entity->country->iso_3166_2; + } + public function phone() { return $this->entity->phone ?: ''; diff --git a/app/Models/SystemLog.php b/app/Models/SystemLog.php index 781e2b1a7a48..082f5280c2b9 100644 --- a/app/Models/SystemLog.php +++ b/app/Models/SystemLog.php @@ -148,6 +148,8 @@ class SystemLog extends Model const TYPE_RAZORPAY = 322; + const TYPE_PAYPAL_PPCP = 323; + const TYPE_QUOTA_EXCEEDED = 400; const TYPE_UPSTREAM_FAILURE = 401; diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php index d0f7456450e6..ad9493865c99 100644 --- a/app/PaymentDrivers/BaseDriver.php +++ b/app/PaymentDrivers/BaseDriver.php @@ -108,6 +108,11 @@ class BaseDriver extends AbstractPaymentDriver return $this; } + public function getAvailableMethods(): array + { + return self::$methods; + } + /** * Required fields for client to fill, to proceed with gateway actions. * diff --git a/app/PaymentDrivers/PayPalPPCPPaymentDriver.php b/app/PaymentDrivers/PayPalPPCPPaymentDriver.php new file mode 100644 index 000000000000..df1f52683c0a --- /dev/null +++ b/app/PaymentDrivers/PayPalPPCPPaymentDriver.php @@ -0,0 +1,512 @@ + 'paypal', + 1 => 'card', + 25 => 'venmo', + // 9 => 'sepa', + // 12 => 'bancontact', + // 17 => 'eps', + // 15 => 'giropay', + // 13 => 'ideal', + // 26 => 'mercadopago', + // 27 => 'mybank', + // 28 => 'paylater', + // 16 => 'p24', + // 7 => 'sofort' + ]; + + /** + * Return an array of + * enabled gateway payment methods + * + * @return array + */ + public function gatewayTypes(): array + { + + return collect($this->company_gateway->fees_and_limits) + ->filter(function ($fee){ + return $fee->is_enabled; + })->map(function ($fee, $key){ + return (int)$key; + })->toArray(); + + } + + private function getPaymentMethod($gateway_type_id): int + { + $method = PaymentType::PAYPAL; + + match($gateway_type_id){ + "1" => $method = PaymentType::CREDIT_CARD_OTHER, + "3" => $method = PaymentType::PAYPAL, + "25" => $method = PaymentType::VENMO, + }; + + return $method; + } + + private function getFundingOptions():string + { + + $enums = [ + 1 => 'card', + 3 => 'paypal', + 25 => 'venmo', + // 9 => 'sepa', + // 12 => 'bancontact', + // 17 => 'eps', + // 15 => 'giropay', + // 13 => 'ideal', + // 26 => 'mercadopago', + // 27 => 'mybank', + // 28 => 'paylater', + // 16 => 'p24', + // 7 => 'sofort' + ]; + + $funding_options = ''; + + foreach($this->company_gateway->fees_and_limits as $key => $value) { + + if($value->is_enabled) { + + $funding_options .=$enums[$key].','; + + } + + } + + return rtrim($funding_options, ','); + + } + + /** + * Initialize the Paypal gateway. + * + * Attempt to generate and return the access token. + * + * @return self + */ + public function init(): self + { + + $this->api_endpoint_url = $this->company_gateway->getConfigField('testMode') ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com'; + + $secret = config('ninja.paypal.secret'); + $client_id = config('ninja.paypal.client_id'); + + if($this->access_token && $this->token_expiry && $this->token_expiry->isFuture()) + return $this; + + $response = Http::withBasicAuth($client_id, $secret) + ->withHeaders(['Content-Type' => 'application/x-www-form-urlencoded']) + ->withQueryParameters(['grant_type' => 'client_credentials']) + ->post("{$this->api_endpoint_url}/v1/oauth2/token"); + + // nlog($response->body()); + + if($response->successful()) { + $this->access_token = $response->json()['access_token']; + $this->token_expiry = now()->addSeconds($response->json()['expires_in'] - 60); + } else { + throw new PaymentFailed('Unable to gain access token from Paypal. Check your configuration', 401); + } + + return $this; + + } + + public function setPaymentMethod($payment_method_id) + { + if(!$payment_method_id) { + return $this; + } + + $this->gateway_type_id = $payment_method_id; + + $this->paypal_payment_method = $this->funding_options[$payment_method_id]; + + return $this; + } + + public function authorizeView($payment_method) + { + // PayPal doesn't support direct authorization. + + return $this; + } + + public function authorizeResponse($request) + { + // PayPal doesn't support direct authorization. + + return $this; + } + + private function checkPaymentsReceivable(): self + { + + if($this->company_gateway->getConfigField('status') != 'activated'){ + + if (class_exists(\Modules\Admin\Services\PayPal\PayPalService::class)) { + $pp = new \Modules\Admin\Services\PayPal\PayPalService($this->company_gateway->company, $this->company_gateway->user); + $pp->updateMerchantStatus($this->company_gateway); + + $this->company_gateway = $this->company_gateway->fresh(); + $config = $this->company_gateway->getConfig(); + + if($config->status == 'activated') + return $this; + + } + + throw new PaymentFailed('Unable to accept payments at this time, please contact PayPal for more information.', 401); + } + + return $this; + + } + + public function processPaymentView($data) + { + $this->init()->checkPaymentsReceivable(); + + $data['gateway'] = $this; + $this->payment_hash->data = array_merge((array) $this->payment_hash->data, ['amount' => $data['total']['amount_with_fee']]); + $this->payment_hash->save(); + + $data['client_id'] = config('ninja.paypal.client_id'); + $data['token'] = $this->getClientToken(); + $data['order_id'] = $this->createOrder($data); + $data['funding_source'] = $this->paypal_payment_method; + $data['gateway_type_id'] = $this->gateway_type_id; + $data['merchantId'] = $this->company_gateway->getConfigField('merchantId'); + + // nlog($data['merchantId']); + + return render('gateways.paypal.ppcp.pay', $data); + + } + + private function getClientToken(): string + { + + $r = $this->gatewayRequest('/v1/identity/generate-token', 'post', ['body' => '']); + + if($r->successful()) + return $r->json()['client_token']; + + throw new PaymentFailed('Unable to gain client token from Paypal. Check your configuration', 401); + + } + + public function processPaymentResponse($request) + { + nlog($request->all()); + + $request['gateway_response'] = str_replace("Error: ", "", $request['gateway_response']); + $response = json_decode($request['gateway_response'], true); + + nlog($response); + + if(isset($response['status']) && $response['status'] == 'COMPLETED' && isset($response['purchase_units'])) { + + $data = [ + 'payment_type' => $this->getPaymentMethod($request->gateway_type_id), + 'amount' => $response['purchase_units'][0]['amount']['value'], + 'transaction_reference' => $response['purchase_units'][0]['payments']['captures'][0]['id'], + 'gateway_type_id' => GatewayType::PAYPAL, + ]; + + $payment = $this->createPayment($data, \App\Models\Payment::STATUS_COMPLETED); + + SystemLogger::dispatch( + ['response' => $response, 'data' => $data], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_SUCCESS, + SystemLog::TYPE_PAYPAL, + $this->client, + $this->client->company, + ); + + return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]); + + } else { + + if(isset($response['headers']) ?? false) + unset($response['headers']); + + SystemLogger::dispatch( + ['response' => $response], + SystemLog::CATEGORY_GATEWAY_RESPONSE, + SystemLog::EVENT_GATEWAY_FAILURE, + SystemLog::TYPE_PAYPAL, + $this->client, + $this->client->company, + ); + + $message = $response['body']['details'][0]['description'] ?? 'Payment failed. Please try again.'; + + throw new PaymentFailed($message, 400); + } + } + + private function paymentSource(): array + { + /** we only need to support paypal as payment source until as we are only using hosted payment buttons */ + return $this->injectPayPalPaymentSource(); + + // return match($this->paypal_payment_method) { + // 'paypal' => $this->injectPayPalPaymentSource(), + // 'card' => $this->injectCardPaymentSource(), + // 'venmo' => $this->injectVenmoPaymentSource(), + // }; + + } + + private function injectVenmoPaymentSource(): array + { + + return [ + "venmo" => [ + "email_address" => $this->client->present()->email(), + ], + ]; + + } + + private function injectCardPaymentSource(): array + { + + return [ + "card" => [ + + "name" => $this->client->present()->name(), + "email_address" => $this->client->present()->email(), + "billing_address" => $this->getBillingAddress(), + "experience_context"=> [ + "user_action" => "PAY_NOW" + ], + ], + ]; + + } + + private function injectPayPalPaymentSource(): array + { + + return [ + "paypal" => [ + + "name" => [ + "given_name" => $this->client->present()->first_name(), + "surname" => $this->client->present()->last_name(), + ], + "email_address" => $this->client->present()->email(), + "address" => $this->getBillingAddress(), + "experience_context"=> [ + "user_action" => "PAY_NOW" + ], + ], + ]; + + } + + private function createOrder(array $data): string + { + + $_invoice = collect($this->payment_hash->data->invoices)->first(); + + $invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id)); + + $description = collect($invoice->line_items)->map(function ($item){ + return $item->notes; + })->implode("\n"); + + $order = [ + + "intent" => "CAPTURE", + "payment_source" => $this->paymentSource(), + "purchase_units" => [ + [ + "description" =>ctrans('texts.invoice_number').'# '.$invoice->number, + "invoice_id" => $invoice->number, + "payee" => [ + "merchant_id" => $this->company_gateway->getConfigField('merchantId'), + ], + "payment_instruction" => [ + "disbursement_mode" => "INSTANT", + ], + $this->getShippingAddress(), + "amount" => [ + "value" => (string)$data['amount_with_fee'], + "currency_code"=> $this->client->currency()->code, + "breakdown" => [ + "item_total" => [ + "currency_code" => $this->client->currency()->code, + "value" => (string)$data['amount_with_fee'] + ] + ] + ], + "items"=> [ + [ + "name" => ctrans('texts.invoice_number').'# '.$invoice->number, + "description" => substr($description, 0, 127), + "quantity" => "1", + "unit_amount" => [ + "currency_code" => $this->client->currency()->code, + "value" => (string)$data['amount_with_fee'] + ], + ], + ], + ], + ] + ]; + + + if($shipping = $this->getShippingAddress()){ + $order['purchase_units'][0] = $shipping; + } + + $r = $this->gatewayRequest('/v2/checkout/orders', 'post', $order); + + nlog($r->json()); + + return $r->json()['id']; + + } + + private function getBillingAddress(): array + { + return + [ + "address_line_1" => $this->client->address1, + "address_line_2" => $this->client->address2, + "admin_area_2" => $this->client->city, + "admin_area_1" => $this->client->state, + "postal_code" => $this->client->postal_code, + "country_code" => $this->client->country->iso_3166_2, + ]; + } + + private function getShippingAddress(): ?array + { + return $this->company_gateway->require_shipping_address ? + [ + "shipping" => [ + "address" => + [ + "address_line_1" => $this->client->shipping_address1, + "address_line_2" => $this->client->shipping_address2, + "admin_area_2" => $this->client->shipping_city, + "admin_area_1" => $this->client->shipping_state, + "postal_code" => $this->client->shipping_postal_code, + "country_code" => $this->client->present()->shipping_country_code(), + ], + ] + ] + : null; + + } + + public function gatewayRequest(string $uri, string $verb, array $data, ?array $headers = []) + { + $this->init(); + + $r = Http::withToken($this->access_token) + ->withHeaders($this->getHeaders($headers)) + ->{$verb}("{$this->api_endpoint_url}{$uri}", $data); + + if($r->successful()) { + return $r; + } + + throw new PaymentFailed("Gateway failure - {$r->body()}", 401); + + } + + private function getHeaders(array $headers = []): array + { + return array_merge([ + 'Accept' => 'application/json', + 'Content-type' => 'application/json', + 'Accept-Language' => 'en_US', + 'PayPal-Partner-Attribution-Id' => 'invoiceninja_SP_PPCP', + 'PayPal-Request-Id' => Str::uuid()->toString(), + ], $headers); + } + + private function feeCalc($invoice, $invoice_total) + { + // $invoice->service()->removeUnpaidGatewayFees(); + // $invoice = $invoice->fresh(); + + // $balance = floatval($invoice->balance); + + // $_updated_invoice = $invoice->service()->addGatewayFee($this->company_gateway, GatewayType::PAYPAL, $invoice_total)->save(); + + // if (floatval($_updated_invoice->balance) > $balance) { + // $fee = floatval($_updated_invoice->balance) - $balance; + + // $this->payment_hash->fee_total = $fee; + // $this->payment_hash->save(); + + // return $fee; + // } + + // return 0; + // } + + + } +} \ No newline at end of file diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php index 336e04f13067..d0395ee6b5cf 100644 --- a/app/PaymentDrivers/StripePaymentDriver.php +++ b/app/PaymentDrivers/StripePaymentDriver.php @@ -140,13 +140,13 @@ class StripePaymentDriver extends BaseDriver return $this; } + /** * Returns the gateway types. */ public function gatewayTypes(): array { $types = [ - // GatewayType::CRYPTO, GatewayType::CREDIT_CARD, ]; diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 8c4448b2304a..25a159d393bd 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -263,13 +263,245 @@ use App\Observers\PaymentObserver; use App\Observers\ProductObserver; use App\Observers\ProjectObserver; use App\Observers\ProposalObserver; +<<<<<<< HEAD +======= +use App\Events\Quote\QuoteWasViewed; +use App\Events\Task\TaskWasArchived; +use App\Events\Task\TaskWasRestored; +use App\Events\User\UserWasArchived; +use App\Events\User\UserWasRestored; +use App\Events\Quote\QuoteWasCreated; +use App\Events\Quote\QuoteWasDeleted; +use App\Events\Quote\QuoteWasEmailed; +use App\Events\Quote\QuoteWasUpdated; +use App\Events\Account\AccountCreated; +use App\Events\Credit\CreditWasViewed; +use App\Events\Invoice\InvoiceWasPaid; +use App\Events\Quote\QuoteWasApproved; +use App\Events\Quote\QuoteWasArchived; +use App\Events\Quote\QuoteWasRestored; +use App\Listeners\LogResponseReceived; +use App\Events\Client\ClientWasCreated; +use App\Events\Client\ClientWasDeleted; +use App\Events\Client\ClientWasUpdated; +use App\Events\Contact\ContactLoggedIn; +use App\Events\Credit\CreditWasCreated; +use App\Events\Credit\CreditWasDeleted; +use App\Events\Credit\CreditWasEmailed; +use App\Events\Credit\CreditWasUpdated; +use App\Events\Design\DesignWasDeleted; +use App\Events\Design\DesignWasUpdated; +use App\Events\Vendor\VendorWasCreated; +use App\Events\Vendor\VendorWasDeleted; +use App\Events\Vendor\VendorWasUpdated; +use App\Observers\CompanyTokenObserver; +use App\Observers\SubscriptionObserver; +use Illuminate\Mail\Events\MessageSent; +use App\Events\Client\ClientWasArchived; +use App\Events\Client\ClientWasRestored; +use App\Events\Credit\CreditWasArchived; +use App\Events\Credit\CreditWasRestored; +use App\Events\Design\DesignWasArchived; +use App\Events\Design\DesignWasRestored; +use App\Events\Invoice\InvoiceWasViewed; +use App\Events\Misc\InvitationWasViewed; +use App\Events\Payment\PaymentWasVoided; +use App\Events\Vendor\VendorWasArchived; +use App\Events\Vendor\VendorWasRestored; +use App\Listeners\Mail\MailSentListener; +use App\Observers\ClientContactObserver; +>>>>>>> paypal_ppcp use App\Observers\PurchaseOrderObserver; use App\Observers\QuoteObserver; use App\Observers\SubscriptionObserver; use App\Observers\TaskObserver; use App\Observers\UserObserver; use App\Observers\VendorContactObserver; +<<<<<<< HEAD use App\Observers\VendorObserver; +======= +use App\Events\Expense\ExpenseWasCreated; +use App\Events\Expense\ExpenseWasDeleted; +use App\Events\Expense\ExpenseWasUpdated; +use App\Events\Invoice\InvoiceWasCreated; +use App\Events\Invoice\InvoiceWasDeleted; +use App\Events\Invoice\InvoiceWasEmailed; +use App\Events\Invoice\InvoiceWasUpdated; +use App\Events\Payment\PaymentWasCreated; +use App\Events\Payment\PaymentWasDeleted; +use App\Events\Payment\PaymentWasEmailed; +use App\Events\Payment\PaymentWasUpdated; +use App\Observers\CompanyGatewayObserver; +use App\Events\Credit\CreditWasMarkedSent; +use App\Events\Expense\ExpenseWasArchived; +use App\Events\Expense\ExpenseWasRestored; +use App\Events\Invoice\InvoiceWasArchived; +use App\Events\Invoice\InvoiceWasRestored; +use App\Events\Invoice\InvoiceWasReversed; +use App\Events\Payment\PaymentWasArchived; +use App\Events\Payment\PaymentWasRefunded; +use App\Events\Payment\PaymentWasRestored; +use Illuminate\Mail\Events\MessageSending; +use App\Events\Document\DocumentWasCreated; +use App\Events\Document\DocumentWasDeleted; +use App\Events\Document\DocumentWasUpdated; +use App\Events\Invoice\InvoiceWasCancelled; +use App\Listeners\Invoice\CreateInvoicePdf; +use App\Listeners\Quote\QuoteEmailActivity; +use App\Listeners\User\CreatedUserActivity; +use App\Listeners\User\DeletedUserActivity; +use App\Listeners\User\UpdatedUserActivity; +use App\Listeners\User\UpdateUserLastLogin; +use App\Events\Account\StripeConnectFailure; +use App\Events\Document\DocumentWasArchived; +use App\Events\Document\DocumentWasRestored; +use App\Events\Invoice\InvoiceWasMarkedSent; +use App\Events\Vendor\VendorContactLoggedIn; +use App\Listeners\Quote\QuoteViewedActivity; +use App\Listeners\LogRequestSending; +use App\Listeners\User\ArchivedUserActivity; +use App\Listeners\User\RestoredUserActivity; +use App\Listeners\Quote\QuoteApprovedWebhook; +use App\Listeners\Quote\QuoteDeletedActivity; +use App\Listeners\Credit\CreditViewedActivity; +use App\Listeners\Invoice\InvoicePaidActivity; +use App\Listeners\Payment\PaymentNotification; +use App\Listeners\Quote\QuoteApprovedActivity; +use App\Listeners\Quote\QuoteArchivedActivity; +use App\Listeners\Quote\QuoteRestoredActivity; +use App\Listeners\Quote\ReachWorkflowSettings; +use App\Events\Company\CompanyDocumentsDeleted; +use App\Listeners\Activity\CreatedTaskActivity; +use App\Listeners\Activity\TaskDeletedActivity; +use App\Listeners\Activity\TaskUpdatedActivity; +use App\Listeners\Invoice\InvoiceEmailActivity; +use App\Listeners\SendVerificationNotification; +use App\Events\Credit\CreditWasEmailedAndFailed; +use App\Listeners\Activity\CreatedQuoteActivity; +use App\Listeners\Activity\DeleteClientActivity; +use App\Listeners\Activity\DeleteCreditActivity; +use App\Listeners\Activity\QuoteUpdatedActivity; +use App\Listeners\Activity\TaskArchivedActivity; +use App\Listeners\Activity\TaskRestoredActivity; +use App\Listeners\Credit\CreditRestoredActivity; +use App\Listeners\Invoice\CreateInvoiceActivity; +use App\Listeners\Invoice\InvoiceViewedActivity; +use App\Listeners\Invoice\UpdateInvoiceActivity; +use App\Listeners\Misc\InvitationViewedListener; +use App\Events\Invoice\InvoiceReminderWasEmailed; +use App\Listeners\Activity\ClientUpdatedActivity; +use App\Listeners\Activity\CreatedClientActivity; +use App\Listeners\Activity\CreatedCreditActivity; +use App\Listeners\Activity\CreatedVendorActivity; +use App\Listeners\Activity\PaymentVoidedActivity; +use App\Listeners\Activity\RestoreClientActivity; +use App\Listeners\Activity\UpdatedCreditActivity; +use App\Listeners\Activity\VendorDeletedActivity; +use App\Listeners\Activity\VendorUpdatedActivity; +use App\Listeners\Contact\UpdateContactLastLogin; +use App\Listeners\Invoice\InvoiceDeletedActivity; +use App\Listeners\Payment\PaymentBalanceActivity; +use App\Listeners\Payment\PaymentEmailedActivity; +use App\Listeners\Quote\QuoteCreatedNotification; +use App\Listeners\Quote\QuoteEmailedNotification; +use Illuminate\Http\Client\Events\RequestSending; +use App\Events\Invoice\InvoiceWasEmailedAndFailed; +use App\Events\Payment\PaymentWasEmailedAndFailed; +use App\Listeners\Activity\ArchivedClientActivity; +use App\Listeners\Activity\CreatedExpenseActivity; +use App\Listeners\Activity\CreditArchivedActivity; +use App\Listeners\Activity\ExpenseDeletedActivity; +use App\Listeners\Activity\ExpenseUpdatedActivity; +use App\Listeners\Activity\PaymentCreatedActivity; +use App\Listeners\Activity\PaymentDeletedActivity; +use App\Listeners\Activity\PaymentUpdatedActivity; +use App\Listeners\Activity\VendorArchivedActivity; +use App\Listeners\Activity\VendorRestoredActivity; +use App\Listeners\Document\DeleteCompanyDocuments; +use App\Listeners\Invoice\InvoiceArchivedActivity; +use App\Listeners\Invoice\InvoiceRestoredActivity; +use App\Listeners\Invoice\InvoiceReversedActivity; +use App\Listeners\Payment\PaymentRestoredActivity; +use App\Listeners\Quote\QuoteApprovedNotification; +use SocialiteProviders\Apple\AppleExtendSocialite; +use SocialiteProviders\Manager\SocialiteWasCalled; +use App\Events\Subscription\SubscriptionWasCreated; +use App\Events\Subscription\SubscriptionWasDeleted; +use App\Events\Subscription\SubscriptionWasUpdated; +use App\Listeners\Activity\ExpenseArchivedActivity; +use App\Listeners\Activity\ExpenseRestoredActivity; +use App\Listeners\Activity\PaymentArchivedActivity; +use App\Listeners\Activity\PaymentRefundedActivity; +use App\Listeners\Credit\CreditCreatedNotification; +use App\Listeners\Credit\CreditEmailedNotification; +use App\Listeners\Invoice\InvoiceCancelledActivity; +use Illuminate\Http\Client\Events\ResponseReceived; +use App\Events\PurchaseOrder\PurchaseOrderWasViewed; +use App\Events\Subscription\SubscriptionWasArchived; +use App\Events\Subscription\SubscriptionWasRestored; +use App\Events\PurchaseOrder\PurchaseOrderWasCreated; +use App\Events\PurchaseOrder\PurchaseOrderWasDeleted; +use App\Events\PurchaseOrder\PurchaseOrderWasEmailed; +use App\Events\PurchaseOrder\PurchaseOrderWasUpdated; +use App\Listeners\Invoice\InvoiceCreatedNotification; +use App\Listeners\Invoice\InvoiceEmailedNotification; +use App\Listeners\Invoice\InvoiceEmailFailedActivity; +use App\Events\PurchaseOrder\PurchaseOrderWasAccepted; +use App\Events\PurchaseOrder\PurchaseOrderWasArchived; +use App\Events\PurchaseOrder\PurchaseOrderWasRestored; +use App\Listeners\Payment\PaymentEmailFailureActivity; +use App\Listeners\Vendor\UpdateVendorContactLastLogin; +use App\Events\RecurringQuote\RecurringQuoteWasCreated; +use App\Events\RecurringQuote\RecurringQuoteWasDeleted; +use App\Events\RecurringQuote\RecurringQuoteWasUpdated; +use App\Listeners\Account\StripeConnectFailureListener; +use App\Listeners\Activity\CreatedSubscriptionActivity; +use App\Listeners\Activity\SubscriptionDeletedActivity; +use App\Listeners\Activity\SubscriptionUpdatedActivity; +use App\Listeners\Invoice\InvoiceReminderEmailActivity; +use App\Events\RecurringQuote\RecurringQuoteWasArchived; +use App\Events\RecurringQuote\RecurringQuoteWasRestored; +use App\Listeners\Activity\SubscriptionArchivedActivity; +use App\Listeners\Activity\SubscriptionRestoredActivity; +use App\Listeners\Invoice\InvoiceFailedEmailNotification; +use SocialiteProviders\Microsoft\MicrosoftExtendSocialite; +use App\Events\RecurringExpense\RecurringExpenseWasCreated; +use App\Events\RecurringExpense\RecurringExpenseWasDeleted; +use App\Events\RecurringExpense\RecurringExpenseWasUpdated; +use App\Events\RecurringInvoice\RecurringInvoiceWasCreated; +use App\Events\RecurringInvoice\RecurringInvoiceWasDeleted; +use App\Events\RecurringInvoice\RecurringInvoiceWasUpdated; +use App\Listeners\PurchaseOrder\PurchaseOrderEmailActivity; +use App\Events\RecurringExpense\RecurringExpenseWasArchived; +use App\Events\RecurringExpense\RecurringExpenseWasRestored; +use App\Events\RecurringInvoice\RecurringInvoiceWasArchived; +use App\Events\RecurringInvoice\RecurringInvoiceWasRestored; +use App\Listeners\PurchaseOrder\CreatePurchaseOrderActivity; +use App\Listeners\PurchaseOrder\PurchaseOrderViewedActivity; +use App\Listeners\PurchaseOrder\UpdatePurchaseOrderActivity; +use App\Listeners\PurchaseOrder\PurchaseOrderCreatedListener; +use App\Listeners\PurchaseOrder\PurchaseOrderDeletedActivity; +use App\Listeners\PurchaseOrder\PurchaseOrderAcceptedActivity; +use App\Listeners\PurchaseOrder\PurchaseOrderAcceptedListener; +use App\Listeners\PurchaseOrder\PurchaseOrderArchivedActivity; +use App\Listeners\PurchaseOrder\PurchaseOrderRestoredActivity; +use App\Listeners\RecurringQuote\CreateRecurringQuoteActivity; +use App\Listeners\RecurringQuote\UpdateRecurringQuoteActivity; +use App\Listeners\RecurringQuote\RecurringQuoteDeletedActivity; +use App\Listeners\RecurringQuote\RecurringQuoteArchivedActivity; +use App\Listeners\RecurringQuote\RecurringQuoteRestoredActivity; +use App\Listeners\PurchaseOrder\PurchaseOrderEmailedNotification; +use App\Listeners\RecurringInvoice\CreateRecurringInvoiceActivity; +use App\Listeners\RecurringInvoice\UpdateRecurringInvoiceActivity; +use App\Listeners\RecurringExpense\CreatedRecurringExpenseActivity; +use App\Listeners\RecurringExpense\RecurringExpenseDeletedActivity; +use App\Listeners\RecurringExpense\RecurringExpenseUpdatedActivity; +use App\Listeners\RecurringInvoice\RecurringInvoiceDeletedActivity; +use App\Listeners\RecurringExpense\RecurringExpenseArchivedActivity; +use App\Listeners\RecurringExpense\RecurringExpenseRestoredActivity; +use App\Listeners\RecurringInvoice\RecurringInvoiceArchivedActivity; +use App\Listeners\RecurringInvoice\RecurringInvoiceRestoredActivity; +>>>>>>> paypal_ppcp use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; use Illuminate\Mail\Events\MessageSending; use Illuminate\Mail\Events\MessageSent; @@ -281,6 +513,13 @@ class EventServiceProvider extends ServiceProvider * */ protected $listen = [ + + RequestSending::class => [ + LogRequestSending::class, + ], + ResponseReceived::class => [ + LogResponseReceived::class, + ], AccountCreated::class => [ ], MessageSending::class => [ diff --git a/config/ninja.php b/config/ninja.php index be246ea105c1..3f48fd1f537d 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -86,6 +86,7 @@ return [ 'password' => 'password', 'stripe' => env('STRIPE_KEYS', ''), 'paypal' => env('PAYPAL_KEYS', ''), + 'ppcp' => env('PPCP_KEYS', ''), 'paypal_rest' => env('PAYPAL_REST_KEYS', ''), 'authorize' => env('AUTHORIZE_KEYS', ''), 'checkout' => env('CHECKOUT_KEYS', ''), @@ -223,4 +224,8 @@ return [ 'client_id' => env('SHOPIFY_CLIENT_ID', null), 'client_secret' => env('SHOPIFY_CLIENT_SECRET', null), ], + 'paypal' => [ + 'secret' => env('PAYPAL_SECRET', null), + 'client_id' => env('PAYPAL_CLIENT_ID', null), + ] ]; diff --git a/database/migrations/2023_10_15_204204_add_paypal_ppcp.php b/database/migrations/2023_10_15_204204_add_paypal_ppcp.php new file mode 100644 index 000000000000..2fdee3ea7715 --- /dev/null +++ b/database/migrations/2023_10_15_204204_add_paypal_ppcp.php @@ -0,0 +1,49 @@ +clientId = ""; + $fields->secret = ""; + $fields->testMode = false; + + $paypal = new Gateway; + $paypal->id = 61; + $paypal->name = 'PayPal Platform'; + $paypal->key = '80af24a6a691230bbec33e930ab40666'; + $paypal->provider = 'PayPal_PPCP'; + $paypal->is_offsite = false; + $paypal->fields = \json_encode($fields); + $paypal->visible = 1; + $paypal->site_url = 'https://www.paypal.com/'; + $paypal->save(); + + } + + Gateway::whereIn('id', [60, 15, 49])->update(['visible' => 0]); + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // + } +}; diff --git a/database/seeders/PaymentLibrariesSeeder.php b/database/seeders/PaymentLibrariesSeeder.php index 6803c4bf5f54..ef83fddaf205 100644 --- a/database/seeders/PaymentLibrariesSeeder.php +++ b/database/seeders/PaymentLibrariesSeeder.php @@ -84,6 +84,7 @@ class PaymentLibrariesSeeder extends Seeder ['id' => 58, 'name' => 'Razorpay', 'provider' => 'Razorpay', 'is_offsite' => false, 'sort_order' => 21, 'key' => 'hxd6gwg3ekb9tb3v9lptgx1mqyg69zu9', 'fields' => '{"apiKey":"","apiSecret":""}'], ['id' => 59, 'name' => 'Forte', 'provider' => 'Forte', 'is_offsite' => false, 'sort_order' => 21, 'key' => 'kivcvjexxvdiyqtj3mju5d6yhpeht2xs', 'fields' => '{"testMode":false,"apiLoginId":"","apiAccessId":"","secureKey":"","authOrganizationId":"","organizationId":"","locationId":""}'], ['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}'], ]; foreach ($gateways as $gateway) { @@ -103,9 +104,8 @@ class PaymentLibrariesSeeder extends Seeder Gateway::whereIn('id', [1,3,7,11,15,20,39,46,55,50,57,52,58,59,60])->update(['visible' => 1]); if (Ninja::isHosted()) { - Gateway::whereIn('id', [20])->update(['visible' => 0]); - Gateway::whereIn('id', [56])->update(['visible' => 1]); - Gateway::whereIn('id', [49])->update(['visible' => 1]); + Gateway::whereIn('id', [20,49])->update(['visible' => 0]); + Gateway::whereIn('id', [56,61])->update(['visible' => 1]); } Gateway::all()->each(function ($gateway) { diff --git a/resources/views/portal/ninja2020/gateways/paypal/ppcp/pay.blade.php b/resources/views/portal/ninja2020/gateways/paypal/ppcp/pay.blade.php new file mode 100644 index 000000000000..508d2285862d --- /dev/null +++ b/resources/views/portal/ninja2020/gateways/paypal/ppcp/pay.blade.php @@ -0,0 +1,69 @@ +@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title' => 'PayPal']) + +@section('gateway_head') + + +@endsection + +@section('gateway_content') +
+ + + + + +@endsection + +@section('gateway_footer') +@endsection + +@push('footer') + + + + + +@endpush \ No newline at end of file