PayPal has release an official SDK to simplify our work. Here I want to show you how to integrate into Laravel 4.

1. Install PayPal SDK via composer

Edit file composer.json

1
2
3
4
5
6
7
8
{
...
"require": {
...
"paypal/rest-api-sdk-php": "*"
},
...
}

Update the dependencies

1
$ php composer.phar update --no-dev

You can now use the PayPal package in the project.

2. Configure PayPal

Add a config file for paypal: app/config/paypal.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
return array(
// set your paypal credential
'client_id' => 'AcT3DS8a-SmTEtSl9hNcwyscoLypndD9q5L0YcfxmaUavz3p_xwFNRE-OauO',
'secret' => 'ENv8_RCXMfhcrzdSfAWjLWDiD_GJSD-Gbm5q2Pj92vIuobCtgLpR3SUxqAhZ',

/**
* SDK configuration
*/
'settings' => array(
/**
* Available option 'sandbox' or 'live'
*/
'mode' => 'sandbox',

/**
* Specify the max request time in seconds
*/
'http.ConnectionTimeOut' => 30,

/**
* Whether want to log to a file
*/
'log.LogEnabled' => true,

/**
* Specify the file that want to write on
*/
'log.FileName' => storage_path() . '/logs/paypal.log',

/**
* Available option 'FINE', 'INFO', 'WARN' or 'ERROR'
*
* Logging is most verbose in the 'FINE' level and decreases as you
* proceed towards ERROR
*/
'log.LogLevel' => 'FINE'
),
);

Setup in app/controllers/IndexController.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
use PayPal\Rest\ApiContext;
use PayPal\Auth\OAuthTokenCredential;
use PayPal\Api\Amount;
use PayPal\Api\Details;
use PayPal\Api\Item;
use PayPal\Api\ItemList;
use PayPal\Api\Payer;
use PayPal\Api\Payment;
use PayPal\Api\RedirectUrls;
use PayPal\Api\ExecutePayment;
use PayPal\Api\PaymentExecution;
use PayPal\Api\Transaction;

class IndexController extends BaseController
{
...
private $_api_context;

public function __construct()
{
...
// setup PayPal api context
$paypal_conf = Config::get('paypal');
$this->_api_context = new ApiContext(new OAuthTokenCredential($paypal_conf['client_id'], $paypal_conf['secret']));
$this->_api_context->setConfig($paypal_conf['settings']);
}
...
}

3. Add 2 routes for processing PayPal checkout

Add this to app/routes.php

1
2
3
4
5
6
7
8
9
10
11
<?php
Route::post('payment', array(
'as' => 'payment',
'uses' => 'IndexController@postPayment',
));

// this is after make the payment, PayPal redirect back to your site
Route::get('payment/status', array(
'as' => 'payment.status',
'uses' => 'IndexController@getPaymentStatus',
));

Update the controller, this method is when you submit the form or checkout shopping cart, then post to this route: app/controllers/IndexController.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?php
public function postPayment()
{
...

$payer = new Payer();
$payer->setPaymentMethod('paypal');

$item_1 = new Item();
$item_1->setName('Item 1') // item name
->setCurrency('USD')
->setQuantity(2)
->setPrice('15'); // unit price

$item_2 = new Item();
$item_2->setName('Item 2')
->setCurrency('USD')
->setQuantity(4)
->setPrice('7');

$item_3 = new Item();
$item_3->setName('Item 3')
->setCurrency('USD')
->setQuantity(1)
->setPrice('20');

// add item to list
$item_list = new ItemList();
$item_list->setItems(array($item_1, $item_2, $item_3));

$amount = new Amount();
$amount->setCurrency('USD')
->setTotal(78);

$transaction = new Transaction();
$transaction->setAmount($amount)
->setItemList($item_list)
->setDescription('Your transaction description');

$redirect_urls = new RedirectUrls();
$redirect_urls->setReturnUrl(URL::route('payment.status'))
->setCancelUrl(URL::route('payment.status'));

$payment = new Payment();
$payment->setIntent('Sale')
->setPayer($payer)
->setRedirectUrls($redirect_urls)
->setTransactions(array($transaction));

try {
$payment->create($this->_api_context);
} catch (\PayPal\Exception\PPConnectionException $ex) {
if (\Config::get('app.debug')) {
echo "Exception: " . $ex->getMessage() . PHP_EOL;
$err_data = json_decode($ex->getData(), true);
exit;
} else {
die('Some error occur, sorry for inconvenient');
}
}

foreach($payment->getLinks() as $link) {
if($link->getRel() == 'approval_url') {
$redirect_url = $link->getHref();
break;
}
}

// add payment ID to session
Session::put('paypal_payment_id', $payment->getId());

if(isset($redirect_url)) {
// redirect to paypal
return Redirect::away($redirect_url);
}

return Redirect::route('original.route')
->with('error', 'Unknown error occurred');
}

PayPal summary

Up to this point, you will see the page above.

PayPal pay

Then login & pay.

4. Add another handler to handle PayPal after payment

Before that, when the payment successfully made, it will return this 2 parameters as query string

1
2
token=EC-05R25178G5276364N
PayerID=LXA67A9A83UD6

Otherwise, ONLY token when the customer cancel the payment

1
token=EC-05R25178G5276364N

app/controllers/IndexController.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
public function getPaymentStatus()
{
// Get the payment ID before session clear
$payment_id = Session::get('paypal_payment_id');

// clear the session payment ID
Session::forget('paypal_payment_id');

if (empty(Input::get('PayerID')) || empty(Input::get('token'))) {
return Redirect::route('original.route')
->with('error', 'Payment failed');
}

$payment = Payment::get($payment_id, $this->_api_context);

// PaymentExecution object includes information necessary
// to execute a PayPal account payment.
// The payer_id is added to the request query parameters
// when the user is redirected from paypal back to your site
$execution = new PaymentExecution();
$execution->setPayerId(Input::get('PayerID'));

//Execute the payment
$result = $payment->execute($execution, $this->_api_context);

echo '<pre>';print_r($result);echo '</pre>';exit; // DEBUG RESULT, remove it later

if ($result->getState() == 'approved') { // payment made
return Redirect::route('original.route')
->with('success', 'Payment success');
}
return Redirect::route('original.route')
->with('error', 'Payment failed');
}

See the DEBUG RESULT there, if you print it out, the output will be

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
PayPal\Api\Payment Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[id] => PAY-24J48306WV121522LKQNZSPI
[create_time] => 2014-09-19T02:47:25Z
[update_time] => 2014-09-19T02:53:55Z
[state] => approved
[intent] => sale
[payer] => PayPal\Api\Payer Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[payment_method] => paypal
[payer_info] => PayPal\Api\PayerInfo Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[email] => john.smith@example.com
[first_name] => John
[last_name] => Smith
[payer_id] => LXA67A9A83UD6
[shipping_address] => PayPal\Api\ShippingAddress Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[line1] => 1 Main Terrace
[line2] =>
[city] => Wolverhampton
[state] => West Midlands
[postal_code] => W12 4LQ
[country_code] => GB
[recipient_name] =>
)

)

)

)

)

)

[transactions] => Array
(
[0] => PayPal\Api\Transaction Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[amount] => PayPal\Api\Amount Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[total] => 78.00
[currency] => USD
[details] => PayPal\Api\Details Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[subtotal] => 78.00
)

)

)

)

[description] => Your item description
[item_list] => PayPal\Api\ItemList Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[items] => Array
(
[0] => PayPal\Api\Item Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[name] => Item 1
[price] => 15.00
[currency] => USD
[quantity] => 2
)

)

[1] => PayPal\Api\Item Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[name] => Item 2
[price] => 7.00
[currency] => USD
[quantity] => 4
)

)

[2] => PayPal\Api\Item Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[name] => Item 3
[price] => 20.00
[currency] => USD
[quantity] => 1
)

)

)

[shipping_address] => PayPal\Api\ShippingAddress Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[recipient_name] =>
[line1] => 1 Main Terrace
[line2] =>
[city] => Wolverhampton
[state] => West Midlands
[postal_code] => W12 4LQ
[country_code] => GB
)

)

)

)

[related_resources] => Array
(
[0] => PayPal\Api\RelatedResources Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[sale] => PayPal\Api\Sale Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[id] => 48629238J1664492L
[create_time] => 2014-09-19T02:47:25Z
[update_time] => 2014-09-19T02:53:55Z
[amount] => PayPal\Api\Amount Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[total] => 78.00
[currency] => USD
)

)

[payment_mode] => INSTANT_TRANSFER
[state] => completed
[protection_eligibility] => ELIGIBLE
[protection_eligibility_type] => ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE
[parent_payment] => PAY-24J48306WV121522LKQNZSPI
[links] => Array
(
[0] => PayPal\Api\Links Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[href] => https://api.sandbox.paypal.com/v1/payments/sale/48629238J1664492L
[rel] => self
[method] => GET
)

)

[1] => PayPal\Api\Links Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[href] => https://api.sandbox.paypal.com/v1/payments/sale/48629238J1664492L/refund
[rel] => refund
[method] => POST
)

)

[2] => PayPal\Api\Links Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[href] => https://api.sandbox.paypal.com/v1/payments/payment/PAY-24J48306WV121522LKQNZSPI
[rel] => parent_payment
[method] => GET
)

)

)

)

)

)

)

)

)

)

)

[links] => Array
(
[0] => PayPal\Api\Links Object
(
[_propMap:PayPal\Common\PPModel:private] => Array
(
[href] => https://api.sandbox.paypal.com/v1/payments/payment/PAY-24J48306WV121522LKQNZSPI
[rel] => self
[method] => GET
)

)

)

)

)

You have done.

Update Dec 17, 2014

If you have problem with live credentials, please read through this section

1. Login to PayPal developer portal

PayPal credential step 1

Click on Dashboard link

2. Edit your app

PayPal credential step 2

Under My apps, click on the app you wanted to use in your project

3. Get the live credentials

PayPal credential step 3

Scroll down after the Add webhook button, click on the Show link

PayPal credential step 4

Then edit app/config/paypal.php, replace client_id & secret with the live credentials, and change the mode to live

Ack: Thanks waiylgeek for the info.

Update Jan 19, 2015. READ ME:

My intention of this post is about to share how to integrate PayPal official SDK to Laravel 4.
I’m not a PayPal expert here, those PayPal api, PayPal rules, etc question please refer to PayPal official website.

If you have any feedback or improvement, welcome to comment on this post, or drop me an email.

Thank you.