mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 05:02:36 +01:00
Working on upgrade for paypal
This commit is contained in:
parent
219e07c1c3
commit
37cc0cf442
@ -11,16 +11,17 @@
|
||||
|
||||
namespace App\Libraries;
|
||||
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\Document;
|
||||
use App\Models\User;
|
||||
use App\Models\Client;
|
||||
use App\Models\Account;
|
||||
use App\Models\Company;
|
||||
use App\Models\Document;
|
||||
use App\Models\PaymentHash;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\VendorContact;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class MultiDB.
|
||||
@ -485,6 +486,27 @@ class MultiDB
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function findAndSetByPaymentHash(string $hash)
|
||||
{
|
||||
if (! config('ninja.db.multi_db_enabled')) {
|
||||
return PaymentHash::with('fee_invoice')->where('hash', $hash)->first();
|
||||
}
|
||||
|
||||
$current_db = config('database.default');
|
||||
|
||||
foreach (self::$dbs as $db) {
|
||||
if ($payment_hash = PaymentHash::on($db)->where('hash', $hash)->first()) {
|
||||
self::setDb($db);
|
||||
|
||||
return $payment_hash;
|
||||
}
|
||||
}
|
||||
|
||||
self::setDB($current_db);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function findAndSetDbByInvitation($entity, $invitation_key)
|
||||
{
|
||||
$class = 'App\Models\\'.ucfirst(Str::camel($entity)).'Invitation';
|
||||
|
78
app/Notifications/Ninja/PayPalUnlinkedTransaction.php
Normal file
78
app/Notifications/Ninja/PayPalUnlinkedTransaction.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Notifications\Ninja;
|
||||
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use Illuminate\Notifications\Messages\SlackMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class PayPalUnlinkedTransaction extends Notification
|
||||
{
|
||||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
public function __construct(private string $order_id, private string $transaction_reference)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function via($notifiable)
|
||||
{
|
||||
return ['slack'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
*
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($notifiable)
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack($notifiable)
|
||||
{
|
||||
$content = "PayPal Order Not Found\n";
|
||||
$content .= "{$this->order_id}\n";
|
||||
$content .= "Transaction ref: {$this->transaction_reference}\n";
|
||||
|
||||
|
||||
return (new SlackMessage)
|
||||
->success()
|
||||
->from(ctrans('texts.notification_bot'))
|
||||
->image('https://app.invoiceninja.com/favicon.png')
|
||||
->content($content);
|
||||
}
|
||||
}
|
382
app/PaymentDrivers/PayPal/PayPalWebhook.php
Normal file
382
app/PaymentDrivers/PayPal/PayPalWebhook.php
Normal file
@ -0,0 +1,382 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\PaymentDrivers\PayPal;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\SystemLog;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\PaymentHash;
|
||||
use App\Models\PaymentType;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use App\Models\CompanyGateway;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use App\Notifications\Ninja\PayPalUnlinkedTransaction;
|
||||
|
||||
class PayPalWebhook implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $tries = 1; //number of retries
|
||||
|
||||
public $deleteWhenMissingModels = true;
|
||||
|
||||
private $gateway_key = '80af24a6a691230bbec33e930ab40666';
|
||||
|
||||
public function __construct(protected array $webhook_request, protected array $headers, protected string $access_token)
|
||||
{
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
|
||||
if($this->verifyWebhook()) {
|
||||
nlog('verified');
|
||||
|
||||
match($this->webhook_request['event_type']) {
|
||||
'CHECKOUT.ORDER.COMPLETED' => $this->checkoutOrderCompleted(),
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
'id' => 'WH-COC11055RA711503B-4YM959094A144403T',
|
||||
'create_time' => '2018-04-16T21:21:49.000Z',
|
||||
'event_type' => 'CHECKOUT.ORDER.COMPLETED',
|
||||
'resource_type' => 'checkout-order',
|
||||
'resource_version' => '2.0',
|
||||
'summary' => 'Checkout Order Completed',
|
||||
'resource' =>
|
||||
array (
|
||||
'id' => '5O190127TN364715T',
|
||||
'status' => 'COMPLETED',
|
||||
'intent' => 'CAPTURE',
|
||||
'gross_amount' =>
|
||||
array (
|
||||
'currency_code' => 'USD',
|
||||
'value' => '100.00',
|
||||
),
|
||||
'payer' =>
|
||||
array (
|
||||
'name' =>
|
||||
array (
|
||||
'given_name' => 'John',
|
||||
'surname' => 'Doe',
|
||||
),
|
||||
'email_address' => 'buyer@example.com',
|
||||
'payer_id' => 'QYR5Z8XDVJNXQ',
|
||||
),
|
||||
'purchase_units' =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
'reference_id' => 'd9f80740-38f0-11e8-b467-0ed5f89f718b',
|
||||
'amount' =>
|
||||
array (
|
||||
'currency_code' => 'USD',
|
||||
'value' => '100.00',
|
||||
),
|
||||
'payee' =>
|
||||
array (
|
||||
'email_address' => 'seller@example.com',
|
||||
),
|
||||
'shipping' =>
|
||||
array (
|
||||
'method' => 'United States Postal Service',
|
||||
'address' =>
|
||||
array (
|
||||
'address_line_1' => '2211 N First Street',
|
||||
'address_line_2' => 'Building 17',
|
||||
'admin_area_2' => 'San Jose',
|
||||
'admin_area_1' => 'CA',
|
||||
'postal_code' => '95131',
|
||||
'country_code' => 'US',
|
||||
),
|
||||
),
|
||||
'payments' =>
|
||||
array (
|
||||
'captures' =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
'id' => '3C679366HH908993F',
|
||||
'status' => 'COMPLETED',
|
||||
'amount' =>
|
||||
array (
|
||||
'currency_code' => 'USD',
|
||||
'value' => '100.00',
|
||||
),
|
||||
'seller_protection' =>
|
||||
array (
|
||||
'status' => 'ELIGIBLE',
|
||||
'dispute_categories' =>
|
||||
array (
|
||||
0 => 'ITEM_NOT_RECEIVED',
|
||||
1 => 'UNAUTHORIZED_TRANSACTION',
|
||||
),
|
||||
),
|
||||
'final_capture' => true,
|
||||
'seller_receivable_breakdown' =>
|
||||
array (
|
||||
'gross_amount' =>
|
||||
array (
|
||||
'currency_code' => 'USD',
|
||||
'value' => '100.00',
|
||||
),
|
||||
'paypal_fee' =>
|
||||
array (
|
||||
'currency_code' => 'USD',
|
||||
'value' => '3.00',
|
||||
),
|
||||
'net_amount' =>
|
||||
array (
|
||||
'currency_code' => 'USD',
|
||||
'value' => '97.00',
|
||||
),
|
||||
),
|
||||
'create_time' => '2018-04-01T21:20:49Z',
|
||||
'update_time' => '2018-04-01T21:20:49Z',
|
||||
'links' =>
|
||||
array (
|
||||
0 =>
|
||||
array (
|
||||
'href' => 'https://api.paypal.com/v2/payments/captures/3C679366HH908993F',
|
||||
'rel' => 'self',
|
||||
'method' => 'GET',
|
||||
),
|
||||
1 =>
|
||||
array (
|
||||
'href' => 'https://api.paypal.com/v2/payments/captures/3C679366HH908993F/refund',
|
||||
'rel' => 'refund',
|
||||
'method' => 'POST',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
'create_time' => '2018-04-01T21:18:49Z',
|
||||
'update_time' => '2018-04-01T21:20:49Z',
|
||||
'links' =>
|
||||
*/
|
||||
private function checkoutOrderCompleted()
|
||||
{
|
||||
$order = $this->webhook_request['resource'];
|
||||
$transaction_reference = $order['purchase_units'][0]['payments']['captures'][0]['id'];
|
||||
$amount = $order['purchase_units'][0]['payments']['captures'][0]['amount']['value'];
|
||||
$payment_hash = MultiDB::findAndSetByPaymentHash($order['purchase_units'][0]['custom_id']);
|
||||
$merchant_id = $order['purchase_units'][0]['payee']['merchant_id'];
|
||||
if(!$payment_hash) {
|
||||
|
||||
$ninja_company = Company::on('db-ninja-01')->find(config('ninja.ninja_default_company_id'));
|
||||
$ninja_company->notification(new PayPalUnlinkedTransaction($order['id'], $transaction_reference))->ninja();
|
||||
return;
|
||||
}
|
||||
|
||||
nlog("payment completed check");
|
||||
if($payment_hash->payment && $payment_hash->payment->status_id == Payment::STATUS_COMPLETED) // Payment made, all good!
|
||||
return;
|
||||
|
||||
nlog("invoice paid check");
|
||||
if($payment_hash->fee_invoice && $payment_hash->fee_invoice->status_id == Invoice::STATUS_PAID){ // Payment made, all good!
|
||||
|
||||
nlog("payment status check");
|
||||
if($payment_hash->payment && $payment_hash->payment->status_id != Payment::STATUS_COMPLETED) { // Make sure the payment is marked as completed
|
||||
$payment_hash->payment->status_id = Payment::STATUS_COMPLETED;
|
||||
$payment_hash->push();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
nlog("create payment check");
|
||||
if($payment_hash->fee_invoice && in_array($payment_hash->fee_invoice->status_id, [Invoice::STATUS_SENT, Invoice::STATUS_PARTIAL])) {
|
||||
|
||||
$payment = Payment::where('transaction_reference', $transaction_reference)->first();
|
||||
|
||||
if(!$payment) { nlog("make payment here!");
|
||||
$payment = $this->createPayment($payment_hash, [
|
||||
'amount' => $amount,
|
||||
'transaction_reference' => $transaction_reference,
|
||||
'merchant_id' => $merchant_id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function getPaymentType($source): int
|
||||
{
|
||||
$method = 'paypal';
|
||||
|
||||
match($source) {
|
||||
"card" => $method = PaymentType::CREDIT_CARD_OTHER,
|
||||
"paypal" => $method = PaymentType::PAYPAL,
|
||||
"venmo" => $method = PaymentType::VENMO,
|
||||
"paylater" => $method = PaymentType::PAY_LATER,
|
||||
default => $method = PaymentType::PAYPAL,
|
||||
};
|
||||
|
||||
return $method;
|
||||
}
|
||||
|
||||
private function createPayment(PaymentHash $payment_hash, array $data)
|
||||
{
|
||||
|
||||
$client = $payment_hash->fee_invoice->client;
|
||||
|
||||
$company_gateway = $this->harvestGateway($client->company, $data['merchant_id']);
|
||||
$driver = $company_gateway->driver($client)->init();
|
||||
$driver->setPaymentHash($payment_hash);
|
||||
|
||||
$order = $driver->getOrder($this->webhook_request['resource']['id']);
|
||||
$source = 'paypal';
|
||||
|
||||
if(isset($order['payment_source'])) {
|
||||
$source = array_key_first($order['payment_source']);
|
||||
}
|
||||
|
||||
$data = [
|
||||
'payment_type' => $this->getPaymentType($source),
|
||||
'amount' => $data['amount'],
|
||||
'transaction_reference' => $data['transaction_reference'],
|
||||
'gateway_type_id' => GatewayType::PAYPAL,
|
||||
];
|
||||
|
||||
$payment = $driver->createPayment($data, \App\Models\Payment::STATUS_COMPLETED);
|
||||
|
||||
SystemLogger::dispatch(
|
||||
['response' => $this->webhook_request, 'data' => $data],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_SUCCESS,
|
||||
SystemLog::TYPE_PAYPAL,
|
||||
$client,
|
||||
$client->company,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
private function harvestGateway(Company $company, string $merchant_id): ?CompanyGateway
|
||||
{
|
||||
$gateway = CompanyGateway::query()
|
||||
->where('company_id', $company->id)
|
||||
->where('gateway_key', $this->gateway_key)
|
||||
->cursor()
|
||||
->first(function ($cg) use ($merchant_id){
|
||||
$config = $cg->getConfig();
|
||||
|
||||
if($config->merchantId == $merchant_id)
|
||||
return $cg;
|
||||
|
||||
});
|
||||
|
||||
return $gateway ?? false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------//
|
||||
private function verifyWebhook(): bool
|
||||
{
|
||||
$request = [
|
||||
'auth_algo' => $this->headers['paypal-auth-algo'],
|
||||
'cert_url' => $this->headers['paypal-cert-url'],
|
||||
'transmission_id' => $this->headers['paypal-transmission-id'],
|
||||
'transmission_sig' => $this->headers['paypal-transmission-sig'],
|
||||
'transmission_time' => $this->headers['paypal-transmission-time'],
|
||||
'webhook_id' => config('ninja.paypal.webhook_id'),
|
||||
'webhook_event' => $this->webhook_request,
|
||||
];
|
||||
|
||||
$headers = [
|
||||
'Accept' => 'application/json',
|
||||
'Content-type' => 'application/json',
|
||||
'Accept-Language' => 'en_US',
|
||||
'PayPal-Partner-Attribution-Id' => 'invoiceninja_SP_PPCP',
|
||||
];
|
||||
|
||||
$r = Http::withToken($this->access_token)
|
||||
->withHeaders($headers)
|
||||
->post("https://api-m.paypal.com/v1/notifications/verify-webhook-signature", $request);
|
||||
|
||||
nlog($r);
|
||||
nlog($r->json());
|
||||
|
||||
if($r->successful() && $r->json()['verification_status'] == 'SUCCESS') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
{
|
||||
"auth_algo": "SHA256withRSA",
|
||||
"cert_url": "cert_url",
|
||||
"transmission_id": "69cd13f0-d67a-11e5-baa3-778b53f4ae55",
|
||||
"transmission_sig": "lmI95Jx3Y9nhR5SJWlHVIWpg4AgFk7n9bCHSRxbrd8A9zrhdu2rMyFrmz+Zjh3s3boXB07VXCXUZy/UFzUlnGJn0wDugt7FlSvdKeIJenLRemUxYCPVoEZzg9VFNqOa48gMkvF+XTpxBeUx/kWy6B5cp7GkT2+pOowfRK7OaynuxUoKW3JcMWw272VKjLTtTAShncla7tGF+55rxyt2KNZIIqxNMJ48RDZheGU5w1npu9dZHnPgTXB9iomeVRoD8O/jhRpnKsGrDschyNdkeh81BJJMH4Ctc6lnCCquoP/GzCzz33MMsNdid7vL/NIWaCsekQpW26FpWPi/tfj8nLA==",
|
||||
"transmission_time": "2016-02-18T20:01:35Z",
|
||||
"webhook_id": "1JE4291016473214C",
|
||||
"webhook_event": {
|
||||
"id": "8PT597110X687430LKGECATA",
|
||||
"create_time": "2013-06-25T21:41:28Z",
|
||||
"resource_type": "authorization",
|
||||
"event_type": "PAYMENT.AUTHORIZATION.CREATED",
|
||||
"summary": "A payment authorization was created",
|
||||
"resource": {
|
||||
"id": "2DC87612EK520411B",
|
||||
"create_time": "2013-06-25T21:39:15Z",
|
||||
"update_time": "2013-06-25T21:39:17Z",
|
||||
"state": "authorized",
|
||||
"amount": {
|
||||
"total": "7.47",
|
||||
"currency": "USD",
|
||||
"details": {
|
||||
"subtotal": "7.47"
|
||||
}
|
||||
},
|
||||
"parent_payment": "PAY-36246664YD343335CKHFA4AY",
|
||||
"valid_until": "2013-07-24T21:39:15Z",
|
||||
"links": [
|
||||
{
|
||||
"href": "https://api-m.paypal.com/v1/payments/authorization/2DC87612EK520411B",
|
||||
"rel": "self",
|
||||
"method": "GET"
|
||||
},
|
||||
{
|
||||
"href": "https://api-m.paypal.com/v1/payments/authorization/2DC87612EK520411B/capture",
|
||||
"rel": "capture",
|
||||
"method": "POST"
|
||||
},
|
||||
{
|
||||
"href": "https://api-m.paypal.com/v1/payments/authorization/2DC87612EK520411B/void",
|
||||
"rel": "void",
|
||||
"method": "POST"
|
||||
},
|
||||
{
|
||||
"href": "https://api-m.paypal.com/v1/payments/payment/PAY-36246664YD343335CKHFA4AY",
|
||||
"rel": "parent_payment",
|
||||
"method": "GET"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
@ -12,16 +12,18 @@
|
||||
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\PaymentType;
|
||||
use App\Models\SystemLog;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Str;
|
||||
use Carbon\Carbon;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\SystemLog;
|
||||
use App\Models\GatewayType;
|
||||
use App\Models\PaymentType;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use App\Exceptions\PaymentFailed;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use App\PaymentDrivers\PayPal\PayPalWebhook;
|
||||
|
||||
class PayPalPPCPPaymentDriver extends BaseDriver
|
||||
{
|
||||
@ -141,8 +143,8 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
||||
public function init(): self
|
||||
{
|
||||
|
||||
$this->api_endpoint_url = 'https://api-m.paypal.com';
|
||||
// $this->api_endpoint_url = 'https://api-m.sandbox.paypal.com';
|
||||
// $this->api_endpoint_url = 'https://api-m.paypal.com';
|
||||
$this->api_endpoint_url = 'https://api-m.sandbox.paypal.com';
|
||||
$secret = config('ninja.paypal.secret');
|
||||
$client_id = config('ninja.paypal.client_id');
|
||||
|
||||
@ -165,8 +167,14 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
public function setPaymentMethod($payment_method_id)
|
||||
|
||||
/**
|
||||
* Payment method setter
|
||||
*
|
||||
* @param mixed $payment_method_id
|
||||
* @return self
|
||||
*/
|
||||
public function setPaymentMethod($payment_method_id): self
|
||||
{
|
||||
if(!$payment_method_id) {
|
||||
return $this;
|
||||
@ -192,7 +200,12 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether payments are enabled on the account
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
private function checkPaymentsReceivable(): self
|
||||
{
|
||||
|
||||
@ -217,7 +230,13 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Presents the Payment View to the client
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return void
|
||||
*/
|
||||
public function processPaymentView($data)
|
||||
{
|
||||
$this->init()->checkPaymentsReceivable();
|
||||
@ -238,7 +257,13 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
||||
return render('gateways.paypal.ppcp.pay', $data);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes the payment response
|
||||
*
|
||||
* @param mixed $request
|
||||
* @return void
|
||||
*/
|
||||
public function processPaymentResponse($request)
|
||||
{
|
||||
|
||||
@ -293,9 +318,21 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
||||
throw new PaymentFailed($message, 400);
|
||||
}
|
||||
}
|
||||
|
||||
public function getOrder(string $order_id)
|
||||
{
|
||||
$this->init();
|
||||
|
||||
$r = $this->gatewayRequest("/v2/checkout/orders/{$order_id}", 'get', ['body' => '']);
|
||||
|
||||
return $r->json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a client token for the payment form.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getClientToken(): string
|
||||
{
|
||||
|
||||
@ -308,7 +345,12 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
||||
throw new PaymentFailed('Unable to gain client token from Paypal. Check your configuration', 401);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builds the payment request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function paymentSource(): array
|
||||
{
|
||||
/** we only need to support paypal as payment source until as we are only using hosted payment buttons */
|
||||
@ -335,7 +377,13 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the PayPal Order object
|
||||
*
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
private function createOrder(array $data): string
|
||||
{
|
||||
|
||||
@ -353,7 +401,8 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
||||
"payment_source" => $this->paymentSource(),
|
||||
"purchase_units" => [
|
||||
[
|
||||
"description" =>ctrans('texts.invoice_number').'# '.$invoice->number,
|
||||
"custom_id" => $this->payment_hash->hash,
|
||||
"description" => ctrans('texts.invoice_number').'# '.$invoice->number,
|
||||
"invoice_id" => $invoice->number,
|
||||
"payee" => [
|
||||
"merchant_id" => $this->company_gateway->getConfigField('merchantId'),
|
||||
@ -432,7 +481,16 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
||||
: null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the gateway request
|
||||
*
|
||||
* @param string $uri
|
||||
* @param string $verb
|
||||
* @param array $data
|
||||
* @param ?array $headers
|
||||
* @return \Illuminate\Http\Client\Response
|
||||
*/
|
||||
public function gatewayRequest(string $uri, string $verb, array $data, ?array $headers = [])
|
||||
{
|
||||
$this->init();
|
||||
@ -448,7 +506,6 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
||||
return $r;
|
||||
}
|
||||
|
||||
|
||||
SystemLogger::dispatch(
|
||||
['response' => $r->body()],
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
@ -461,7 +518,13 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
||||
throw new PaymentFailed("Gateway failure - {$r->body()}", 401);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates the request headers
|
||||
*
|
||||
* @param array $headers
|
||||
* @return array
|
||||
*/
|
||||
private function getHeaders(array $headers = []): array
|
||||
{
|
||||
return array_merge([
|
||||
@ -473,8 +536,13 @@ class PayPalPPCPPaymentDriver extends BaseDriver
|
||||
], $headers);
|
||||
}
|
||||
|
||||
private function feeCalc($invoice, $invoice_total)
|
||||
public function processWebhookRequest(Request $request)
|
||||
{
|
||||
// nlog($request->all());
|
||||
// nlog($request->headers->all());
|
||||
$this->init();
|
||||
|
||||
PayPalWebhook::dispatch($request->all(), $request->headers->all(), $this->access_token);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -300,115 +300,6 @@ class PayPalRestPaymentDriver extends BaseDriver
|
||||
], $headers);
|
||||
}
|
||||
|
||||
/*
|
||||
public function processPaymentResponse($request)
|
||||
{
|
||||
$this->initializeOmnipayGateway();
|
||||
|
||||
$response = $this->omnipay_gateway
|
||||
->completePurchase(['amount' => $this->payment_hash->data->amount, 'currency' => $this->client->getCurrencyCode()])
|
||||
->send();
|
||||
|
||||
if ($response->isCancelled() && $this->client->getSetting('enable_client_portal')) {
|
||||
return redirect()->route('client.invoices.index')->with('warning', ctrans('texts.status_cancelled'));
|
||||
} elseif ($response->isCancelled() && !$this->client->getSetting('enable_client_portal')) {
|
||||
redirect()->route('client.invoices.show', ['invoice' => $this->payment_hash->fee_invoice])->with('warning', ctrans('texts.status_cancelled'));
|
||||
}
|
||||
|
||||
if ($response->isSuccessful()) {
|
||||
$data = [
|
||||
'payment_method' => $response->getData()['TOKEN'],
|
||||
'payment_type' => PaymentType::PAYPAL,
|
||||
'amount' => $this->payment_hash->data->amount,
|
||||
'transaction_reference' => $response->getTransactionReference(),
|
||||
'gateway_type_id' => GatewayType::PAYPAL,
|
||||
];
|
||||
|
||||
$payment = $this->createPayment($data, \App\Models\Payment::STATUS_COMPLETED);
|
||||
|
||||
SystemLogger::dispatch(
|
||||
['response' => (array) $response->getData(), '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)]);
|
||||
}
|
||||
|
||||
if (! $response->isSuccessful()) {
|
||||
$data = $response->getData();
|
||||
|
||||
$this->sendFailureMail($response->getMessage() ?: '');
|
||||
|
||||
$message = [
|
||||
'server_response' => $data['L_LONGMESSAGE0'],
|
||||
'data' => $this->payment_hash->data,
|
||||
];
|
||||
|
||||
SystemLogger::dispatch(
|
||||
$message,
|
||||
SystemLog::CATEGORY_GATEWAY_RESPONSE,
|
||||
SystemLog::EVENT_GATEWAY_FAILURE,
|
||||
SystemLog::TYPE_PAYPAL,
|
||||
$this->client,
|
||||
$this->client->company,
|
||||
);
|
||||
|
||||
throw new PaymentFailed($response->getMessage(), $response->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
public function generatePaymentDetails(array $data)
|
||||
{
|
||||
$_invoice = collect($this->payment_hash->data->invoices)->first();
|
||||
$invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id));
|
||||
|
||||
// $this->fee = $this->feeCalc($invoice, $data['total']['amount_with_fee']);
|
||||
|
||||
return [
|
||||
'currency' => $this->client->getCurrencyCode(),
|
||||
'transactionType' => 'Purchase',
|
||||
'clientIp' => request()->getClientIp(),
|
||||
// 'amount' => round(($data['total']['amount_with_fee'] + $this->fee),2),
|
||||
'amount' => round($data['total']['amount_with_fee'], 2),
|
||||
'returnUrl' => route('client.payments.response', [
|
||||
'company_gateway_id' => $this->company_gateway->id,
|
||||
'payment_hash' => $this->payment_hash->hash,
|
||||
'payment_method_id' => GatewayType::PAYPAL,
|
||||
]),
|
||||
'cancelUrl' => $this->client->company->domain()."/client/invoices/{$invoice->hashed_id}",
|
||||
'description' => implode(',', collect($this->payment_hash->data->invoices)
|
||||
->map(function ($invoice) {
|
||||
return sprintf('%s: %s', ctrans('texts.invoice_number'), $invoice->invoice_number);
|
||||
})->toArray()),
|
||||
'transactionId' => $this->payment_hash->hash.'-'.time(),
|
||||
'ButtonSource' => 'InvoiceNinja_SP',
|
||||
'solutionType' => 'Sole',
|
||||
];
|
||||
}
|
||||
|
||||
public function generatePaymentItems(array $data)
|
||||
{
|
||||
$_invoice = collect($this->payment_hash->data->invoices)->first();
|
||||
$invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id));
|
||||
|
||||
$items = [];
|
||||
|
||||
$items[] = new Item([
|
||||
'name' => ' ',
|
||||
'description' => ctrans('texts.invoice_number').'# '.$invoice->number,
|
||||
'price' => $data['total']['amount_with_fee'],
|
||||
'quantity' => 1,
|
||||
]);
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
private function feeCalc($invoice, $invoice_total)
|
||||
{
|
||||
$invoice->service()->removeUnpaidGatewayFees();
|
||||
|
@ -227,5 +227,6 @@ return [
|
||||
'paypal' => [
|
||||
'secret' => env('PAYPAL_SECRET', null),
|
||||
'client_id' => env('PAYPAL_CLIENT_ID', null),
|
||||
'webhook_id' => env('PAYPAL_WEBHOOK_ID', null),
|
||||
]
|
||||
];
|
||||
|
@ -113,6 +113,7 @@ use App\Http\Controllers\UserController;
|
||||
use App\Http\Controllers\VendorController;
|
||||
use App\Http\Controllers\WebCronController;
|
||||
use App\Http\Controllers\WebhookController;
|
||||
use App\PaymentDrivers\PayPalPPCPPaymentDriver;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::group(['middleware' => ['throttle:api', 'api_secret_check']], function () {
|
||||
@ -426,5 +427,6 @@ Route::post('api/v1/yodlee/refresh_updates', [YodleeController::class, 'refreshU
|
||||
Route::post('api/v1/yodlee/balance', [YodleeController::class, 'balanceWebhook'])->middleware('throttle:100,1');
|
||||
|
||||
Route::get('api/v1/protected_download/{hash}', [ProtectedDownloadController::class, 'index'])->name('protected_download')->middleware('throttle:300,1');
|
||||
Route::post('api/v1/ppcp/webhook', [PayPalPPCPPaymentDriver::class, 'processWebhookRequest'])->middleware('throttle:1000,1');
|
||||
|
||||
Route::fallback([BaseController::class, 'notFound'])->middleware('throttle:404');
|
||||
|
Loading…
Reference in New Issue
Block a user