mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-04 03:57:32 -05: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;
 | 
					    public const SYSTEM_LOG_TYPE = SystemLog::TYPE_PAYPAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private string $api_endpoint_url = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function gatewayTypes()
 | 
					    public function gatewayTypes()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return [
 | 
					        return [
 | 
				
			||||||
@ -45,16 +47,6 @@ class PayPalRestPaymentDriver extends BaseDriver
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function init()
 | 
					    public function init()
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return $this;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Initialize Omnipay PayPal_Express gateway.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return void
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private function initializeOmnipayGateway(): self
 | 
					 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->omnipay_gateway = Omnipay::create(
 | 
					        $this->omnipay_gateway = Omnipay::create(
 | 
				
			||||||
            $this->company_gateway->gateway->provider
 | 
					            $this->company_gateway->gateway->provider
 | 
				
			||||||
@ -62,6 +54,8 @@ class PayPalRestPaymentDriver extends BaseDriver
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $this->omnipay_gateway->initialize((array) $this->company_gateway->getConfig());
 | 
					        $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;
 | 
					        return $this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -89,32 +83,121 @@ class PayPalRestPaymentDriver extends BaseDriver
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public function processPaymentView($data)
 | 
					    public function processPaymentView($data)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->initializeOmnipayGateway();
 | 
					        $this->init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $data['gateway'] = $this;
 | 
					        $data['gateway'] = $this;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        $this->payment_hash->data = array_merge((array) $this->payment_hash->data, ['amount' => $data['total']['amount_with_fee']]);
 | 
					        $this->payment_hash->data = array_merge((array) $this->payment_hash->data, ['amount' => $data['total']['amount_with_fee']]);
 | 
				
			||||||
        $this->payment_hash->save();
 | 
					        $this->payment_hash->save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $access_token = $this->omnipay_gateway->getToken();
 | 
					        $data['client_id'] = $this->company_gateway->getConfigField('clientId');
 | 
				
			||||||
 | 
					        $data['token'] = $this->getClientToken();
 | 
				
			||||||
        $headers = [
 | 
					        $data['order_id'] = $this->createOrder($data);
 | 
				
			||||||
            '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);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return render('gateways.paypal.pay', $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)
 | 
					    public function processPaymentResponse($request)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->initializeOmnipayGateway();
 | 
					        $this->initializeOmnipayGateway();
 | 
				
			||||||
@ -221,6 +304,8 @@ class PayPalRestPaymentDriver extends BaseDriver
 | 
				
			|||||||
        return $items;
 | 
					        return $items;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private function feeCalc($invoice, $invoice_total)
 | 
					    private function feeCalc($invoice, $invoice_total)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $invoice->service()->removeUnpaidGatewayFees();
 | 
					        $invoice->service()->removeUnpaidGatewayFees();
 | 
				
			||||||
@ -241,4 +326,6 @@ class PayPalRestPaymentDriver extends BaseDriver
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return 0;
 | 
					        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')])
 | 
					@extends('portal.ninja2020.layout.payments', ['gateway_title' => ctrans('texts.payment_type_credit_card'), 'card_title' => ctrans('texts.payment_type_credit_card')])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@section('gateway_head')
 | 
					@section('gateway_head')
 | 
				
			||||||
    <link href="{{ asset('css/card-js.min.css') }}" rel="stylesheet" type="text/css">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <link
 | 
					    <link
 | 
				
			||||||
      rel="stylesheet"
 | 
					      rel="stylesheet"
 | 
				
			||||||
      type="text/css"
 | 
					      type="text/css"
 | 
				
			||||||
@ -12,6 +10,15 @@
 | 
				
			|||||||
@endsection
 | 
					@endsection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@section('gateway_content')
 | 
					@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 id="paypal-button-container" class="paypal-button-container"></div>
 | 
				
			||||||
    <div class="card_container">
 | 
					    <div class="card_container">
 | 
				
			||||||
@ -101,83 +108,16 @@
 | 
				
			|||||||
@endsection
 | 
					@endsection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@push('footer')
 | 
					@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>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<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()) {
 | 
					if (paypal.HostedFields.isEligible()) {
 | 
				
			||||||
  let orderId;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Renders card fields
 | 
					 | 
				
			||||||
  paypal.HostedFields.render({ 
 | 
					  paypal.HostedFields.render({ 
 | 
				
			||||||
    // Call your server to set up the transaction
 | 
					       
 | 
				
			||||||
    createOrder: () => {
 | 
					      createOrder: function(data, actions) {
 | 
				
			||||||
      return fetch("/api/orders", {
 | 
					        return "{!! $order_id !!}"  
 | 
				
			||||||
        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: {
 | 
					      styles: {
 | 
				
			||||||
      ".valid": {
 | 
					      ".valid": {
 | 
				
			||||||
@ -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>
 | 
					</script>
 | 
				
			||||||
@endpush
 | 
					@endpush
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user