1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
24:
25: namespace mcfedr\Paypal;
26:
27: use mcfedr\Paypal\Exceptions\UnsupportedRefundException;
28:
29: 30: 31:
32: class Paypal {
33:
34: 35: 36: 37:
38: private $authentication;
39:
40: 41: 42: 43:
44: private $settings;
45:
46: 47: 48: 49: 50: 51:
52: public function __construct(Authentication $authentication, Settings $settings = null) {
53: $this->authentication = $authentication;
54: if ($settings == null) {
55: $settings = new Settings();
56: }
57: $this->settings = $settings;
58: }
59:
60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73:
74: public function getButton($products, $paidURL, $cancelURL, $notifyURL = null, $invoiceId = null, $custom = null, Buyer $buyer = null, $label = "Checkout") {
75: $action = $this->getButtonAction();
76: $params = $this->getButtonParams($products, $paidURL, $cancelURL, $notifyURL, $invoiceId, $custom, $buyer);
77: $ret = "<form action=\"$action\" method=\"post\">";
78: foreach ($params as $key => $value) {
79: $ret .= "<input type=\"hidden\" name=\"$key\" value=\"$value\"/>";
80: }
81: $ret .= "<button type=\"submit\">$label</button>";
82: $ret .= "</form>";
83: return $ret;
84: }
85:
86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100:
101: public function getButtonParams($products, $paidURL, $cancelURL, $notifyURL = null, $invoiceId = null, $custom = null, Buyer $buyer = null) {
102: $params = array();
103: if ($products instanceof Products\Subscription) {
104: $params['cmd'] = '_xclick-subscriptions';
105: $products->setParams($params);
106: }
107: else if (!is_array($products) || count($products) == 1) {
108: if (!is_array($products)) {
109: $product = $products;
110: }
111: else {
112: $product = $products[0];
113: }
114: $params['cmd'] = '_xclick';
115: $product->setParams($params);
116: }
117: else {
118: $params['cmd'] = '_cart';
119: $params['upload'] = 1;
120: $i = 1;
121: foreach ($products as $product) {
122: $product->setParams($params, "_$i");
123: $i++;
124: }
125: }
126:
127: $params['return'] = $paidURL;
128: $params['cancel_return'] = $cancelURL;
129:
130: if (!empty($notifyURL)) {
131: $params['notify_url'] = $notifyURL;
132: }
133: if (!empty($custom)) {
134: $params['custom'] = $custom;
135: }
136: if (!empty($invoiceId)) {
137: $params['invoice'] = $invoiceId;
138: }
139:
140: if (!empty($buyer)) {
141: $buyer->setParams($params);
142: }
143:
144: $this->settings->setParams($params);
145:
146: $params['business'] = $this->authentication->getEmail();
147: return $params;
148: }
149:
150: 151: 152: 153: 154:
155: public function getButtonAction() {
156: if ($this->authentication->isSandbox()) {
157: return 'https://www.sandbox.paypal.com/cgi-bin/webscr';
158: }
159: else {
160: return 'https://www.paypal.com/cgi-bin/webscr';
161: }
162: }
163:
164: 165: 166: 167: 168: 169: 170: 171: 172: 173:
174: public function handleNotification($vars = null) {
175: if (is_null($vars)) {
176: $vars = $_REQUEST;
177: }
178: $handled = false;
179:
180: if (isset($vars['txn_type'])) {
181: switch ($vars['txn_type']) {
182: case Notifications\Notification::TXT_CART:
183: case Notifications\Notification::TXT_WEB_ACCEPT:
184: $handled = new Notifications\CartNotification($vars);
185: break;
186: case Notifications\Notification::TXT_MASSPAY:
187: $handled = new Notifications\MasspayNotifications($vars);
188: break;
189: case Notifications\Notification::TXT_SUBSCRIPTION_CANCEL:
190: case Notifications\Notification::TXT_SUBSCRIPTION_EXPIRE:
191: case Notifications\Notification::TXT_SUBSCRIPTION_FAILED:
192: case Notifications\Notification::TXT_SUBSCRIPTION_MODIFY:
193: case Notifications\Notification::TXT_SUBSCRIPTION_PAYMENT:
194: case Notifications\Notification::TXT_SUBSCRIPTION_START:
195: $handled = new Notifications\SubscriptionNotification($vars);
196: break;
197: }
198: }
199: else if (isset($vars['payment_status'])) {
200: switch ($vars['payment_status']) {
201: case 'Refunded':
202: case 'Reversed':
203: case 'Canceled_Reversal':
204: $handled = new Notifications\CartChangeNotification($vars);
205: break;
206: }
207: }
208: else if (isset($vars['transaction_type'])) {
209: switch($vars['transaction_type']) {
210: case Notifications\Notification::TXT_ADAPTIVE_CREATE:
211: case Notifications\Notification::TXT_ADAPTIVE_ADJUSTMENT:
212: $handled = new Notifications\AdaptivePaymentNotification($vars);
213: break;
214: }
215: }
216:
217: if (!$handled) {
218: return false;
219: }
220:
221: if (!$this->verifyNotification($handled, $vars)) {
222: return false;
223: }
224:
225:
226: $handled->isOK($this->authentication, $this->settings);
227:
228: if ($this->settings->logNotifications === true) {
229: error_log('paypal notification ' . http_build_query($vars));
230: }
231: else if ($this->settings->logNotifications) {
232: $this->settings->logNotifications->info('Paypal Notification', array(
233: 'vars' => $vars
234: ));
235: }
236: return $handled;
237: }
238:
239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253:
254: public function sendPayment($email, $amount, $id = '', $note = '', $subject = null) {
255: $params = array();
256: $params['RECEIVERTYPE'] = 'EmailAddress';
257: $params['EMAILSUBJECT'] = substr($subject, 0, 255);
258: if (is_array($email)) {
259: $count = count($email);
260:
261: $amountArray = is_array($amount);
262: if ($amountArray && count($amount) != $count) {
263: return false;
264: }
265:
266: $idArray = is_array($id);
267: if ($idArray && count($id) != $count) {
268: return false;
269: }
270:
271: $noteArray = is_array($note);
272: if ($noteArray && count($note) != $count) {
273: return false;
274: }
275:
276: for ($i = 0; $i < $count; $i++) {
277: $params["L_EMAIL$i"] = $email[$i];
278: if ($amountArray) {
279: $params["L_AMT$i"] = $amount[$i];
280: }
281: else {
282: $params["L_AMT$i"] = $amount;
283: }
284:
285: if ($idArray) {
286: $params["L_UNIQUEID$i"] = substr($id[$i], 0, 30);
287: }
288: else {
289: $params["L_UNIQUEID$i"] = substr($id, 0, 30);
290: }
291:
292: if ($noteArray) {
293: $params["L_NOTE$i"] = substr($note[$i], 0, 4000);
294: }
295: else {
296: $params["L_NOTE$i"] = substr($note, 0, 4000);
297: }
298: }
299: }
300: else {
301: $params['L_EMAIL0'] = $email;
302: $params['L_AMT0'] = $amount;
303: $params['L_UNIQUEID0'] = substr($id, 0, 30);
304: $params['L_NOTE0'] = substr($note, 0, 4000);
305: }
306: $params['CURRENCYCODE'] = $this->settings->currency;
307: $response = $this->callPaypalNVP('MassPay', $params);
308: if ($response !== false) {
309: if ($response['ACK'] == 'Success') {
310: return true;
311: }
312: else {
313: throw new Exceptions\MasspayException($response);
314: }
315: }
316: else {
317: return false;
318: }
319: }
320:
321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331:
332: public function refundPayment($transactionId, $invoiceId = null, $type = 'Full') {
333: $params = array();
334: $params['TRANSACTIONID'] = $transactionId;
335: $params['INVOICEID'] = $invoiceId;
336: if($type != 'Full') {
337: throw new UnsupportedRefundException($type);
338: }
339: $params['REFUNDTYPE'] = $type;
340: $response = $this->callPaypalNVP('RefundTransaction', $params);
341: if ($response !== false) {
342: if ($response['ACK'] == 'Success') {
343: return true;
344: }
345: else {
346: throw new Exceptions\RefundException($response);
347: }
348: }
349: else {
350: return false;
351: }
352: }
353:
354: 355: 356: 357: 358: 359: 360: 361:
362: private function callPaypalNVP($method, $params) {
363: $headerParams = array();
364: if ($this->authentication->isSandbox()) {
365: $url = 'https://api-3t.sandbox.paypal.com/nvp';
366: }
367: else {
368: $url = 'https://api-3t.paypal.com/nvp';
369: }
370: $headerParams['USER'] = $this->authentication->getUsername();
371: $headerParams['PWD'] = $this->authentication->getPassword();
372: $headerParams['SIGNATURE'] = $this->authentication->getSignature();
373: $headerParams['VERSION'] = '71.0';
374:
375: $data = http_build_query(array_merge(array('METHOD' => $method), $headerParams, $params));
376:
377: $response = $this->makeRequest($url, $data);
378: if ($response === false) {
379: return false;
380: }
381:
382: parse_str($response, $nvpResArray);
383: return $nvpResArray;
384: }
385:
386: 387: 388: 389: 390: 391: 392: 393: 394:
395: private function verifyNotification($notification, $vars) {
396: if ($this->authentication->isSandbox()) {
397: $url = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
398: }
399: else {
400: $url = 'https://www.paypal.com/cgi-bin/webscr';
401: }
402: $data = http_build_query(array_merge(array('cmd' => '_notify-validate'), $vars));
403: $response = $this->makeRequest($url, $data);
404: if ($response === false) {
405: return false;
406: }
407: $verified = $response == 'VERIFIED';
408: if (!$verified) {
409: throw new Exceptions\NotificationVerificationException($response, $notification);
410: }
411: return $verified;
412: }
413:
414: 415: 416: 417: 418: 419: 420: 421:
422: private function makeRequest($url, $data) {
423:
424: $ch = curl_init();
425: curl_setopt($ch, CURLOPT_URL, $url);
426:
427:
428: curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
429: curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
430:
431: curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
432: curl_setopt($ch, CURLOPT_POST, 1);
433:
434:
435: curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
436:
437:
438: $response = curl_exec($ch);
439:
440: if (curl_errno($ch)) {
441: throw new Exceptions\CurlException($ch, $url, $data);
442: }
443: else {
444:
445: curl_close($ch);
446: }
447: return $response;
448: }
449:
450: }
451: