mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-10-31 16:37:31 -04:00 
			
		
		
		
	Working on Paypal REST integration
This commit is contained in:
		
							parent
							
								
									8e62c5ac51
								
							
						
					
					
						commit
						f647139918
					
				| @ -37,6 +37,8 @@ class PayPalRestPaymentDriver extends BaseDriver | ||||
| 
 | ||||
|     public const SYSTEM_LOG_TYPE = SystemLog::TYPE_PAYPAL; | ||||
| 
 | ||||
|     private string $api_endpoint_url = ''; | ||||
| 
 | ||||
|     public function gatewayTypes() | ||||
|     { | ||||
|         return [ | ||||
| @ -45,16 +47,6 @@ class PayPalRestPaymentDriver extends BaseDriver | ||||
|     } | ||||
| 
 | ||||
|     public function init() | ||||
|     { | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Initialize Omnipay PayPal_Express gateway. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     private function initializeOmnipayGateway(): self | ||||
|     { | ||||
|         $this->omnipay_gateway = Omnipay::create( | ||||
|             $this->company_gateway->gateway->provider | ||||
| @ -62,6 +54,8 @@ class PayPalRestPaymentDriver extends BaseDriver | ||||
| 
 | ||||
|         $this->omnipay_gateway->initialize((array) $this->company_gateway->getConfig()); | ||||
| 
 | ||||
|         $this->api_endpoint_url = $this->company_gateway->getConfigField('testMode') ? 'https://api-m.sandbox.paypal.com' : 'https://api-m.paypal.com'; | ||||
|          | ||||
|         return $this; | ||||
|     } | ||||
| 
 | ||||
| @ -89,32 +83,121 @@ class PayPalRestPaymentDriver extends BaseDriver | ||||
| 
 | ||||
|     public function processPaymentView($data) | ||||
|     { | ||||
|         $this->initializeOmnipayGateway(); | ||||
|         $this->init(); | ||||
| 
 | ||||
|         $data['gateway'] = $this; | ||||
|          | ||||
|         $this->payment_hash->data = array_merge((array) $this->payment_hash->data, ['amount' => $data['total']['amount_with_fee']]); | ||||
|         $this->payment_hash->save(); | ||||
| 
 | ||||
|         $access_token = $this->omnipay_gateway->getToken(); | ||||
| 
 | ||||
|         $headers = [ | ||||
|             'Accept' => 'application/json', | ||||
|             'Content-type' => 'application/json', | ||||
|             'Accept-Language' => 'en_US', | ||||
|         ]; | ||||
| 
 | ||||
|         $r = Http::withToken($access_token) | ||||
|                 ->withHeaders($headers) | ||||
|                 ->post("https://api-m.sandbox.paypal.com/v1/identity/generate-token",['body' => '']); | ||||
| 
 | ||||
|         nlog($r->body()); | ||||
|         dd($r); | ||||
|         $data['client_id'] = $this->company_gateway->getConfigField('clientId'); | ||||
|         $data['token'] = $this->getClientToken(); | ||||
|         $data['order_id'] = $this->createOrder($data); | ||||
| 
 | ||||
|         return render('gateways.paypal.pay', $data); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public function processPaymentResponse($request) | ||||
|     { | ||||
|         $this->init(); | ||||
| 
 | ||||
|         nlog($request->all()); | ||||
|          | ||||
|         $response = json_decode($request['gateway_response'], true); | ||||
|          | ||||
|         $order_id = $response['orderID']; | ||||
| 
 | ||||
|         nlog($order_id); | ||||
| 
 | ||||
|         $r = $this->gatewayRequest("/v2/checkout/orders/{$order_id}/capture", 'post', []); | ||||
| 
 | ||||
|             dd($r->body()); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     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); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private function createOrder(array $data): string | ||||
|     { | ||||
| 
 | ||||
|         $_invoice = collect($this->payment_hash->data->invoices)->first(); | ||||
| 
 | ||||
|         $invoice = Invoice::withTrashed()->find($this->decodePrimaryKey($_invoice->invoice_id)); | ||||
| 
 | ||||
|         $order = [ | ||||
|           "intent" => "CAPTURE", | ||||
|           "purchase_units" => [ | ||||
|                 [ | ||||
|             "description" =>ctrans('texts.invoice_number').'# '.$invoice->number, | ||||
|             "invoice_id" => $invoice->number, | ||||
|             'reference_id' => 'PUHF', | ||||
|             'description' => 'Sporting Goods', | ||||
|             'custom_id' => 'CUST-HighFashions', | ||||
|             'soft_descriptor' => 'HighFashions', | ||||
|             "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, | ||||
|                     "quantity" => "1", | ||||
|                     "unit_amount" => [ | ||||
|                         "currency_code" => $this->client->currency()->code, | ||||
|                         "value" => (string)$data['amount_with_fee'] | ||||
|                     ], | ||||
|                 ], | ||||
|             ], | ||||
|           ] | ||||
|           ] | ||||
|         ]; | ||||
|          | ||||
|         $r = $this->gatewayRequest('/v2/checkout/orders', 'post', $order); | ||||
| 
 | ||||
|         return $r->json()['id']; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public function gatewayRequest(string $uri, string $verb, array $data, ?array $headers = []) | ||||
|     { | ||||
|         $r = Http::withToken($this->omnipay_gateway->getToken()) | ||||
|                 ->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', | ||||
|         ], $headers); | ||||
|     } | ||||
| 
 | ||||
|     /* | ||||
|     public function processPaymentResponse($request) | ||||
|     { | ||||
|         $this->initializeOmnipayGateway(); | ||||
| @ -221,6 +304,8 @@ class PayPalRestPaymentDriver extends BaseDriver | ||||
|         return $items; | ||||
|     } | ||||
| 
 | ||||
|     */ | ||||
| 
 | ||||
|     private function feeCalc($invoice, $invoice_total) | ||||
|     { | ||||
|         $invoice->service()->removeUnpaidGatewayFees(); | ||||
| @ -241,4 +326,6 @@ class PayPalRestPaymentDriver extends BaseDriver | ||||
| 
 | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
| } | ||||
|  | ||||
| @ -1,8 +1,6 @@ | ||||
| @extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title' => ctrans('texts.payment_type_credit_card')]) | ||||
| 
 | ||||
| @section('gateway_head') | ||||
|     <link href="{{ asset('css/card-js.min.css') }}" rel="stylesheet" type="text/css"> | ||||
| 
 | ||||
|     <link | ||||
|       rel="stylesheet" | ||||
|       type="text/css" | ||||
| @ -12,7 +10,16 @@ | ||||
| @endsection | ||||
| 
 | ||||
| @section('gateway_content') | ||||
|      | ||||
|     <form action="{{ route('client.payments.response') }}" method="post" id="server_response"> | ||||
|         @csrf | ||||
|         <input type="hidden" name="payment_hash" value="{{ $payment_hash }}"> | ||||
|         <input type="hidden" name="company_gateway_id" value="{{ $gateway->company_gateway->id }}"> | ||||
|         <input type="hidden" name="gateway_response" id="gateway_response"> | ||||
|         <input type="hidden" name="amount_with_fee" id="amount_with_fee" value="{{ $total['amount_with_fee'] }}"/> | ||||
|     </form> | ||||
| 
 | ||||
|     <div class="alert alert-failure mb-4" hidden id="errors"></div> | ||||
| 
 | ||||
| <div id="paypal-button-container" class="paypal-button-container"></div> | ||||
|     <div class="card_container"> | ||||
|       <form id="card-form"> | ||||
| @ -101,85 +108,18 @@ | ||||
| @endsection | ||||
| 
 | ||||
| @push('footer') | ||||
| <script src="https://www.paypal.com/sdk/js?components=buttons,hosted-fields&client-id=AdRZZt44vJYAtXirmzMjnvUMoFIloN9kpuSgshQB7SJqLHbgtMP_rcmhy83FYY4a-c3R-_e4wZC8E3oG" data-client-token="{!! $token !!}"> | ||||
| <script src="https://www.paypal.com/sdk/js?components=buttons,hosted-fields&intent=capture&client-id={!! $client_id !!}" data-client-token="{!! $token !!}"> | ||||
| </script> | ||||
| 
 | ||||
| <script> | ||||
| paypal | ||||
|   .Buttons({ | ||||
|     // Sets up the transaction when a payment button is clicked
 | ||||
|     createOrder: function () { | ||||
|       return fetch("/api/orders", { | ||||
|         method: "post", | ||||
|         // use the "body" param to optionally pass additional order information
 | ||||
|         // like product skus and quantities
 | ||||
|         body: JSON.stringify({ | ||||
|           cart: [ | ||||
|             { | ||||
|               sku: "213434", | ||||
|               quantity: "1", | ||||
|             }, | ||||
|           ], | ||||
|         }), | ||||
|       }) | ||||
|         .then((response) => response.json()) | ||||
|         .then((order) => order.id); | ||||
|     }, | ||||
|     // Finalize the transaction after payer approval
 | ||||
|     onApprove: function (data) { | ||||
|       return fetch(`/api/orders/${data.orderID}/capture`, { | ||||
|         method: "post", | ||||
|       }) | ||||
|         .then((response) => response.json()) | ||||
|         .then((orderData) => { | ||||
|           // Successful capture! For dev/demo purposes:
 | ||||
|           console.log( | ||||
|             "Capture result", | ||||
|             orderData, | ||||
|             JSON.stringify(orderData, null, 2) | ||||
|           ); | ||||
|           const transaction = orderData.purchase_units[0].payments.captures[0]; | ||||
|           alert(`Transaction ${transaction.status}: ${transaction.id} | ||||
| 
 | ||||
|             See console for all available details | ||||
|           `); | ||||
|           // When ready to go live, remove the alert and show a success message within this page. For example:
 | ||||
|           // var element = document.getElementById('paypal-button-container');
 | ||||
|           // element.innerHTML = '<h3>Thank you for your payment!</h3>';
 | ||||
|           // Or go to another URL:  actions.redirect('thank_you.html');
 | ||||
|         }); | ||||
|     }, | ||||
|   }) | ||||
|   .render("#paypal-button-container"); | ||||
| 
 | ||||
| // If this returns false or the card fields aren't visible, see Step #1.
 | ||||
| if (paypal.HostedFields.isEligible()) { | ||||
|   let orderId; | ||||
| 
 | ||||
|   // Renders card fields
 | ||||
|   paypal.HostedFields.render({ | ||||
|     // Call your server to set up the transaction
 | ||||
|     createOrder: () => { | ||||
|       return fetch("/api/orders", { | ||||
|         method: "post", | ||||
|         // use the "body" param to optionally pass additional order information
 | ||||
|         // like product skus and quantities
 | ||||
|         body: JSON.stringify({ | ||||
|           cart: [ | ||||
|             { | ||||
|               sku: "cxcxcx", | ||||
|               quantity: "333", | ||||
|             }, | ||||
|           ], | ||||
|         }), | ||||
|       }) | ||||
|         .then((res) => res.json()) | ||||
|         .then((orderData) => { | ||||
|           orderId = orderData.id; // needed later to complete capture
 | ||||
|           return orderData.id; | ||||
|         }); | ||||
|     }, | ||||
|     styles: { | ||||
|   paypal.HostedFields.render({  | ||||
|         | ||||
|       createOrder: function(data, actions) { | ||||
|         return "{!! $order_id !!}"   | ||||
|       }, | ||||
|       styles: { | ||||
|       ".valid": { | ||||
|         color: "green", | ||||
|       }, | ||||
| @ -261,9 +201,34 @@ if (paypal.HostedFields.isEligible()) { | ||||
|         }); | ||||
|     }); | ||||
|   }); | ||||
| } else { | ||||
|   // Hides card fields if the merchant isn't eligible
 | ||||
|   document.querySelector("#card-form").style = "display: none"; | ||||
| } | ||||
| else { | ||||
|     document.querySelector("#card-form").style = "display: none"; | ||||
| 
 | ||||
|     paypal.Buttons({  | ||||
|  env: 'sandbox', // sandbox | production
 | ||||
| 
 | ||||
| client: { | ||||
|     sandbox:    "{{ $gateway->company_gateway->getConfigField('clientId') }}", | ||||
| 
 | ||||
| },        | ||||
|       createOrder: function(data, actions) { | ||||
|         return "{!! $order_id !!}"   | ||||
|       }, | ||||
|       onApprove: function(data, actions) { | ||||
| 
 | ||||
|         return actions.order.capture().then(function(details) {                                     | ||||
|            | ||||
|           console.log(details); | ||||
|         });            | ||||
| 
 | ||||
|             // document.getElementById("gateway_response").value =JSON.stringify( data );
 | ||||
|             // document.getElementById("server_response").submit();
 | ||||
| 
 | ||||
|       } | ||||
|     }).render('#paypal-button-container'); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| </script> | ||||
| @endpush | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user