diff --git a/app/Mail/Engine/BaseEmailEngine.php b/app/Mail/Engine/BaseEmailEngine.php
index 56155107e7eb..af43f66d4e95 100644
--- a/app/Mail/Engine/BaseEmailEngine.php
+++ b/app/Mail/Engine/BaseEmailEngine.php
@@ -126,7 +126,7 @@ class BaseEmailEngine implements EngineInterface
if (! empty($this->variables)) {
- $text = str_replace(['$paymentLink', '$viewButton', '$view_button', '$viewLink', '$view_link'], '$view_url', $text);
+ $text = str_replace(['$paymentLink', '$viewButton', '$view_button', '$viewLink', '$view_link'], "\r\n\r\n".'$view_url'."\r\n", $text);
$text = str_replace(array_keys($this->variables), array_values($this->variables), $text);
$text = str_replace(array_keys($this->variables), array_values($this->variables), $text);
diff --git a/app/PaymentDrivers/PayPal/PayPalBasePaymentDriver.php b/app/PaymentDrivers/PayPal/PayPalBasePaymentDriver.php
index dc5f845a0c76..b9ecf3802dc7 100644
--- a/app/PaymentDrivers/PayPal/PayPalBasePaymentDriver.php
+++ b/app/PaymentDrivers/PayPal/PayPalBasePaymentDriver.php
@@ -16,6 +16,7 @@ use Carbon\Carbon;
use App\Models\Invoice;
use App\Models\SystemLog;
use App\Models\GatewayType;
+use App\Models\PaymentHash;
use App\Models\PaymentType;
use Illuminate\Http\Request;
use App\Jobs\Util\SystemLogger;
@@ -30,6 +31,8 @@ class PayPalBasePaymentDriver extends BaseDriver
{
use MakesHash;
+ public string $risk_guid;
+
public $token_billing = true;
public $can_authorise_credit_card = false;
@@ -106,6 +109,7 @@ class PayPalBasePaymentDriver extends BaseDriver
public function init()
{
+ $this->risk_guid = Str::random(32);
$this->api_endpoint_url = $this->company_gateway->getConfigField('testMode') ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com';
@@ -432,6 +436,7 @@ class PayPalBasePaymentDriver extends BaseDriver
'Accept-Language' => 'en_US',
'PayPal-Partner-Attribution-Id' => 'invoiceninja_SP_PPCP',
'PayPal-Request-Id' => Str::uuid()->toString(),
+ 'PAYPAL-CLIENT-METADATA-ID' => $this->risk_guid,
], $headers);
}
@@ -479,5 +484,5 @@ class PayPalBasePaymentDriver extends BaseDriver
PayPalWebhook::dispatch($request->all(), $request->headers->all(), $this->access_token);
}
-
+
}
\ No newline at end of file
diff --git a/app/PaymentDrivers/PayPalPPCPPaymentDriver.php b/app/PaymentDrivers/PayPalPPCPPaymentDriver.php
index 3f29b8146516..ad8c764f37b6 100644
--- a/app/PaymentDrivers/PayPalPPCPPaymentDriver.php
+++ b/app/PaymentDrivers/PayPalPPCPPaymentDriver.php
@@ -16,6 +16,7 @@ use Carbon\Carbon;
use App\Models\Invoice;
use App\Models\SystemLog;
use App\Models\GatewayType;
+use App\Models\PaymentHash;
use App\Models\PaymentType;
use Illuminate\Http\Request;
use App\Jobs\Util\SystemLogger;
@@ -84,6 +85,7 @@ class PayPalPPCPPaymentDriver extends PayPalBasePaymentDriver
*/
public function processPaymentView($data)
{
+
$this->init()->checkPaymentsReceivable();
$data['gateway'] = $this;
@@ -97,6 +99,7 @@ class PayPalPPCPPaymentDriver extends PayPalBasePaymentDriver
$data['gateway_type_id'] = $this->gateway_type_id;
$data['merchantId'] = $this->company_gateway->getConfigField('merchantId');
$data['currency'] = $this->client->currency()->code;
+ $data['guid'] = $this->risk_guid;
if($this->gateway_type_id == 29)
return render('gateways.paypal.ppcp.card', $data);
@@ -109,7 +112,6 @@ class PayPalPPCPPaymentDriver extends PayPalBasePaymentDriver
* Processes the payment response
*
* @param mixed $request
- * @return void
*/
public function processPaymentResponse($request)
{
@@ -366,4 +368,73 @@ class PayPalPPCPPaymentDriver extends PayPalBasePaymentDriver
return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]);
}
+
+ public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
+ {
+ $data = [];
+ $this->payment_hash = $payment_hash;
+
+ $data['amount_with_fee'] = $this->payment_hash->data->amount_with_fee;
+ $data["payment_source"] = [
+ "card" => [
+ "vault_id" => $cgt->token,
+ "stored_credential" => [
+ "payment_initiator" => "MERCHANT",
+ "payment_type" => "UNSCHEDULED",
+ "usage" => "SUBSEQUENT",
+ ],
+ ],
+ ];
+
+ $orderId = $this->createOrder($data);
+
+ $r = false;
+
+ try {
+
+ $r = $this->gatewayRequest("/v2/checkout/orders/{$orderId}", 'get', ['body' => '']);
+
+ if($r->status() == 422) {
+ //handle conditions where the client may need to try again.
+
+ $r = $this->handleDuplicateInvoiceId($orderId);
+
+
+ }
+
+ } catch(\Exception $e) {
+
+ //Rescue for duplicate invoice_id
+ if(stripos($e->getMessage(), 'DUPLICATE_INVOICE_ID') !== false) {
+
+
+ $r = $this->handleDuplicateInvoiceId($orderId);
+
+ }
+
+ }
+
+ $response = $r->json();
+
+ $data = [
+ 'payment_type' => $this->getPaymentMethod((string)$cgt->gateway_type_id),
+ 'amount' => $response['purchase_units'][0]['payments']['captures'][0]['amount']['value'],
+ 'transaction_reference' => $response['purchase_units'][0]['payments']['captures'][0]['id'],
+ 'gateway_type_id' => $this->gateway_type_id,
+ ];
+
+ $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_PPCP,
+ $this->client,
+ $this->client->company,
+ );
+
+
+ }
+
}
\ No newline at end of file
diff --git a/app/PaymentDrivers/PayPalRestPaymentDriver.php b/app/PaymentDrivers/PayPalRestPaymentDriver.php
index 5e06c72d2422..fc0198037ffa 100644
--- a/app/PaymentDrivers/PayPalRestPaymentDriver.php
+++ b/app/PaymentDrivers/PayPalRestPaymentDriver.php
@@ -20,6 +20,7 @@ use App\Jobs\Util\SystemLogger;
use App\Utils\Traits\MakesHash;
use App\Exceptions\PaymentFailed;
use App\Models\ClientGatewayToken;
+use App\Models\PaymentHash;
use App\PaymentDrivers\PayPal\PayPalBasePaymentDriver;
class PayPalRestPaymentDriver extends PayPalBasePaymentDriver
@@ -30,6 +31,7 @@ class PayPalRestPaymentDriver extends PayPalBasePaymentDriver
public function processPaymentView($data)
{
+
$this->init();
$data['gateway'] = $this;
@@ -43,6 +45,7 @@ class PayPalRestPaymentDriver extends PayPalBasePaymentDriver
$data['funding_source'] = $this->paypal_payment_method;
$data['gateway_type_id'] = $this->gateway_type_id;
$data['currency'] = $this->client->currency()->code;
+ $data['guid'] = $this->risk_guid;
if($this->gateway_type_id == 29)
return render('gateways.paypal.ppcp.card', $data);
@@ -56,7 +59,6 @@ class PayPalRestPaymentDriver extends PayPalBasePaymentDriver
* processPaymentResponse
*
* @param mixed $request
- * @return void
*/
public function processPaymentResponse($request)
{
@@ -291,7 +293,6 @@ class PayPalRestPaymentDriver extends PayPalBasePaymentDriver
*
* @param mixed $request
* @param array $response
- * @return void
*/
public function processTokenPayment($request, array $response) {
@@ -342,4 +343,72 @@ class PayPalRestPaymentDriver extends PayPalBasePaymentDriver
return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]);
}
+
+ public function tokenBilling(ClientGatewayToken $cgt, PaymentHash $payment_hash)
+ {
+ $data = [];
+ $this->payment_hash = $payment_hash;
+
+ $data['amount_with_fee'] = $this->payment_hash->data->amount_with_fee;
+ $data["payment_source"] = [
+ "card" => [
+ "vault_id" => $cgt->token,
+ "stored_credential" => [
+ "payment_initiator" => "MERCHANT",
+ "payment_type" => "UNSCHEDULED",
+ "usage" => "SUBSEQUENT",
+ ],
+ ],
+ ];
+
+ $orderId = $this->createOrder($data);
+
+ $r = false;
+
+ try {
+
+ $r = $this->gatewayRequest("/v2/checkout/orders/{$orderId}", 'get', ['body' => '']);
+
+ if($r->status() == 422) {
+ //handle conditions where the client may need to try again.
+
+ $r = $this->handleDuplicateInvoiceId($orderId);
+
+
+ }
+
+ } catch(\Exception $e) {
+
+ //Rescue for duplicate invoice_id
+ if(stripos($e->getMessage(), 'DUPLICATE_INVOICE_ID') !== false) {
+
+
+ $r = $this->handleDuplicateInvoiceId($orderId);
+
+ }
+
+ }
+
+ $response = $r->json();
+
+ $data = [
+ 'payment_type' => $this->getPaymentMethod((string)$cgt->gateway_type_id),
+ 'amount' => $response['purchase_units'][0]['payments']['captures'][0]['amount']['value'],
+ 'transaction_reference' => $response['purchase_units'][0]['payments']['captures'][0]['id'],
+ 'gateway_type_id' => $this->gateway_type_id,
+ ];
+
+ $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_PPCP,
+ $this->client,
+ $this->client->company,
+ );
+
+
+ }
}
diff --git a/app/Services/Email/EmailDefaults.php b/app/Services/Email/EmailDefaults.php
index 79c7dcf812a3..877ce2b605bd 100644
--- a/app/Services/Email/EmailDefaults.php
+++ b/app/Services/Email/EmailDefaults.php
@@ -180,7 +180,7 @@ class EmailDefaults
$breaks = ["
","
","
"];
$this->email->email_object->text_body = str_ireplace($breaks, "\r\n", $this->email->email_object->body);
$this->email->email_object->text_body = strip_tags($this->email->email_object->text_body);
- $this->email->email_object->text_body = str_replace(['$view_button','$viewButton'], '$view_url', $this->email->email_object->text_body);
+ $this->email->email_object->text_body = str_replace(['$view_button','$viewButton'], "\r\n\r\n".'$view_url'."\r\n", $this->email->email_object->text_body);
if ($this->template == 'email.template.custom') {
$this->email->email_object->body = (str_replace('$body', $this->email->email_object->body, str_replace(["\r","\n"], "", $this->email->email_object->settings->email_style_custom)));
diff --git a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php
index 588ed1123de7..4b3ebce7e95f 100644
--- a/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php
+++ b/resources/views/portal/ninja2020/gateways/paypal/pay.blade.php
@@ -29,6 +29,17 @@
@endsection
@push('footer')
+
+
+
+
+
+