├── src
├── php
│ └── Rbs
│ │ └── Payment
│ │ ├── Template
│ │ ├── LoaderInterface.php
│ │ ├── Loader
│ │ │ ├── String.php
│ │ │ └── Filesystem.php
│ │ └── Parser.php
│ │ ├── Factory.php
│ │ ├── Fields.php
│ │ ├── templates
│ │ └── basic.html
│ │ ├── Http
│ │ └── Client.php
│ │ └── Gateway
│ │ ├── TwoCheckout.php
│ │ ├── Paypal.php
│ │ └── AbstractGateway.php
└── tests
│ └── unit-tests
│ ├── bootstrap.php
│ ├── phpunit.xml
│ └── Rbs
│ └── Payment
│ ├── FactoryTest.php
│ └── Gateway
│ └── PaypalTest.php
├── examples
├── twocheckout.php
└── paypal.php
└── README.md
/src/php/Rbs/Payment/Template/LoaderInterface.php:
--------------------------------------------------------------------------------
1 |
10 |
11 |
--------------------------------------------------------------------------------
/src/php/Rbs/Payment/Template/Loader/String.php:
--------------------------------------------------------------------------------
1 | source = $source;
12 | }
13 |
14 | public function load()
15 | {
16 | if (!empty($this->source)) {
17 | throw new \InvalidArgumentException("Provided source is empty.");
18 | }
19 |
20 | return $this->source;
21 | }
22 | }
--------------------------------------------------------------------------------
/src/php/Rbs/Payment/Factory.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf("Rbs\\Payment\\Gateway\\AbstractGateway", $factory);
9 | }
10 |
11 | /**
12 | * @expectedException InvalidArgumentException
13 | */
14 | public function testDoesNotLoadInvalidGateway()
15 | {
16 | $factory = \Rbs\Payment\Factory::factory('Invalid');
17 | }
18 | }
--------------------------------------------------------------------------------
/src/php/Rbs/Payment/Template/Parser.php:
--------------------------------------------------------------------------------
1 | loader = $loader;
12 | }
13 |
14 | public function parse($placeholders = array())
15 | {
16 | $output = $this->loader->load();
17 |
18 | foreach ($placeholders as $key => $value) {
19 | $needle = '{' . $key . '}';
20 | $output = str_replace($needle, $value, $output);
21 | }
22 |
23 | return $output;
24 | }
25 | }
--------------------------------------------------------------------------------
/src/php/Rbs/Payment/Template/Loader/Filesystem.php:
--------------------------------------------------------------------------------
1 | source = $source;
12 | }
13 |
14 | public function load()
15 | {
16 | if (!file_exists($this->source)) {
17 | throw new \InvalidArgumentException("Provided file source does not exist.");
18 | }
19 |
20 | $contents = file_get_contents($this->source);
21 | return $contents;
22 | }
23 | }
--------------------------------------------------------------------------------
/src/php/Rbs/Payment/Fields.php:
--------------------------------------------------------------------------------
1 | fields)) {
12 | return $this->fields[$key];
13 | }
14 | }
15 |
16 | public function __set($key, $value)
17 | {
18 | $this->fields[$key] = $value;
19 | }
20 |
21 | public function __isset($key)
22 | {
23 | return array_key_exists($key, $this->fields);
24 | }
25 |
26 | public function getAll()
27 | {
28 | return $this->fields;
29 | }
30 | }
--------------------------------------------------------------------------------
/examples/twocheckout.php:
--------------------------------------------------------------------------------
1 | setAccountIdentifier(array('sid' => 1234567, 'secret' => 'tango'));
11 | $twoCheckout->setCurrency('USD');
12 | $twoCheckout->setSingleItem('T-shirt', 4.99, "1001");
13 | $twoCheckout->setOrderNumber(100);
14 |
15 | $twoCheckout->setReturnOnSuccessUrl('http://phpfour.com/payment/twocheckout_success.php');
16 |
17 | $twoCheckout->proceed();
--------------------------------------------------------------------------------
/examples/paypal.php:
--------------------------------------------------------------------------------
1 | setAccountIdentifier('payment@test.com');
11 | $paypal->setCurrency('USD');
12 | $paypal->setSingleItem('T-shirt', 4.99, "1001");
13 | $paypal->setOrderNumber(100);
14 |
15 | $paypal->setReturnOnSuccessUrl('http://phpfour.com/payment/paypal_success.php');
16 | $paypal->setReturnOnFailureUrl('http://phpfour.com/payment/paypal_failure.php');
17 | $paypal->setNotificationUrl('http://phpfour.com/payment/paypal_ipn.php');
18 |
19 | $paypal->proceed();
--------------------------------------------------------------------------------
/src/php/Rbs/Payment/templates/basic.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {pageTitle}
6 |
20 |
25 |
26 |
27 | {pageTitle}
28 |
29 | {primaryMessage}
30 | {paymentForm}
31 |
32 |
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | PHP Payment
2 | ===========
3 |
4 | A payment library for PHP that supports Paypal, Authorize.net and 2Checkout.
5 |
6 | * Easy to integrate
7 | * Supports single order checkout
8 | * Unified interface for all payment gateways
9 | * Template for redirection page
10 |
11 | Usage
12 | =====
13 |
14 | Initiating a paypal checkout
15 |
16 | $paypal = \Rbs\Payment\Factory::factory('Paypal');
17 |
18 | $paypal->setAccountIdentifier('emran@rightbrainsolution.com');
19 | $paypal->setCurrency('USD');
20 | $paypal->setSingleItem('T-shirt', 4.99, "1001");
21 |
22 | $paypal->setReturnOnSuccessUrl('http://phpfour.com/payment/paypal_success.php');
23 | $paypal->setReturnOnFailureUrl('http://phpfour.com/payment/paypal_failure.php');
24 | $paypal->setNotificationUrl('http://phpfour.com/payment/paypal_ipn.php');
25 |
26 | $paypal->proceed();
27 |
28 | Verifying paypal IPN response
29 |
30 | $paypal = \Rbs\Payment\Factory::factory('Paypal');
31 | $client = \Rbs\Payment\Http\Client();
32 |
33 | $paypal->populate($_POST);
34 | $paypal->setHttpClient($client);
35 |
36 | $status = $paypal->verify();
37 |
38 | More updates coming soon.
39 |
40 | Dependencies
41 | ============
42 |
43 | To run the unit tests, you must have the following excellent libraries installed via PEAR:
44 |
45 | * PHPUnit - https://github.com/sebastianbergmann/phpunit
46 | * Mockery - https://github.com/padraic/mockery
47 | * Gradwell Autoloader - https://github.com/Gradwell/Autoloader
--------------------------------------------------------------------------------
/src/php/Rbs/Payment/Http/Client.php:
--------------------------------------------------------------------------------
1 | url = $url;
15 | }
16 |
17 | public function setPort($port)
18 | {
19 | $this->port = $port;
20 | }
21 |
22 | public function setData($data = array())
23 | {
24 | if (!empty($data)) {
25 | $this->data = $data;
26 | }
27 | }
28 |
29 | public function setMethod($method)
30 | {
31 | $this->method = strtoupper($method);
32 | }
33 |
34 | public function sendRequest()
35 | {
36 | $parsedUrl = parse_url($this->url);
37 |
38 | $postString = '';
39 | foreach ($this->data as $key => $value) {
40 | $postString .= $key .'=' . urlencode(stripslashes($value)) . '&';
41 | }
42 |
43 | $fp = fsockopen($parsedUrl['host'], $this->port, $errNum, $errStr, 30);
44 |
45 | if (!$fp) {
46 | throw new \Exception("Cannot open socket.");
47 | }
48 |
49 | fputs($fp, "{$this->method} {$parsedUrl['path']} HTTP/1.1\r\n");
50 | fputs($fp, "Host: {$parsedUrl['host']}\r\n");
51 | fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
52 | fputs($fp, "Content-length: " . strlen($postString) . "\r\n");
53 | fputs($fp, "Connection: close\r\n\r\n");
54 | fputs($fp, $postString . "\r\n\r\n");
55 |
56 | $response = '';
57 | while(!feof($fp)) {
58 | $response .= fgets($fp, 1024);
59 | }
60 |
61 | fclose($fp);
62 |
63 | return $response;
64 | }
65 | }
--------------------------------------------------------------------------------
/src/php/Rbs/Payment/Gateway/TwoCheckout.php:
--------------------------------------------------------------------------------
1 | gatewayUrl = 'https://www.2checkout.com/checkout/purchase';
10 | $this->setTestMode(false);
11 | }
12 |
13 | public function setCurrency($currency)
14 | {
15 | if (!empty($currency)) {
16 | $this->fields->tco_currency = $currency;
17 | }
18 | }
19 |
20 | public function setAccountIdentifier($identifier)
21 | {
22 | if (!empty($identifier)) {
23 | $this->fields->sid = $identifier['sid'];
24 | $this->fields->secret = $identifier['secret'];
25 | }
26 | }
27 |
28 | public function setReturnOnSuccessUrl($url)
29 | {
30 | if (!empty($url)) {
31 | $this->fields->x_Receipt_Link_URL = $url;
32 | }
33 | }
34 |
35 | public function setReturnOnFailureUrl($url)
36 | {
37 | throw new \Exception("2CO does not support a failure URL.");
38 | }
39 |
40 | public function setNotificationUrl($url)
41 | {
42 | throw new \Exception("2CO does not support an IPN URL.");
43 | }
44 |
45 | public function setCustomField($key, $value)
46 | {
47 | if (!empty($key) && !empty($value)) {
48 | $this->fields->$key = $value;
49 | }
50 | }
51 |
52 | public function setSingleItem($name, $amount, $id = null)
53 | {
54 | if (!empty($name)) {
55 | $this->fields->item_name = $name;
56 | }
57 |
58 | if (!empty($amount)) {
59 | $this->fields->total = $amount;
60 | }
61 | }
62 |
63 | public function setOrderNumber($orderNumber)
64 | {
65 | if (!empty($orderNumber)) {
66 | $this->fields->cart_order_id = $orderNumber;
67 | }
68 | }
69 |
70 | public function setTestMode($mode)
71 | {
72 | if ($mode) {
73 | $this->fields->demo = "Y";
74 | $this->testMode = true;
75 | }
76 | }
77 |
78 | protected function validate()
79 | {
80 | $requiredFields = array(
81 | 'sid',
82 | 'tco_currency',
83 | 'x_Receipt_Link_URL',
84 | 'total',
85 | 'cart_order_id'
86 | );
87 |
88 | foreach ($requiredFields as $field) {
89 | if (!isset($this->fields->$field)) {
90 | throw new \Exception("Required parameter {$field} has not been set.");
91 | }
92 | }
93 |
94 | return true;
95 | }
96 |
97 | public function verify()
98 | {
99 | $vendorNumber = ($this->fields->vendor_number != '') ? $this->fields->vendor_number : $this->fields->sid;
100 | $orderNumber = $this->fields->order_number;
101 | $orderTotal = $this->fields->total;
102 |
103 | // If demo mode, the order number must be forced to 1
104 | if($this->testMode) {
105 | $orderNumber = "1";
106 | }
107 |
108 | // Calculate md5 hash as 2co formula: md5(secret_word + vendor_number + order_number + total)
109 | $key = strtoupper(md5($this->fields->secret . $vendorNumber . $orderNumber . $orderTotal));
110 |
111 | // Verify if the key is accurate
112 | if($this->fields->key == $key || $this->fields->x_MD5_Hash == $key) {
113 | return true;
114 | } else {
115 | return false;
116 | }
117 | }
118 | }
--------------------------------------------------------------------------------
/src/php/Rbs/Payment/Gateway/Paypal.php:
--------------------------------------------------------------------------------
1 | setTestMode(false);
10 | $this->setReturnMethodAsPost();
11 | $this->setCommandAsClick();
12 | }
13 |
14 | private function setCommandAsClick()
15 | {
16 | $this->fields->cmd = '_xclick';
17 | }
18 |
19 | private function setReturnMethodAsPost()
20 | {
21 | $this->fields->rm = '2';
22 | }
23 |
24 | public function setCurrency($currency)
25 | {
26 | if (!empty($currency)) {
27 | $this->fields->currency_code = $currency;
28 | }
29 | }
30 |
31 | public function setAccountIdentifier($identifier)
32 | {
33 | if (!empty($identifier)) {
34 | $this->fields->business = $identifier;
35 | }
36 | }
37 |
38 | public function setReturnOnSuccessUrl($url)
39 | {
40 | if (!empty($url)) {
41 | $this->fields->return = $url;
42 | }
43 | }
44 |
45 | public function setReturnOnFailureUrl($url)
46 | {
47 | if (!empty($url)) {
48 | $this->fields->cancel_return = $url;
49 | }
50 | }
51 |
52 | public function setNotificationUrl($url)
53 | {
54 | if (!empty($url)) {
55 | $this->fields->notify_url = $url;
56 | }
57 | }
58 |
59 | public function setCustomField($key, $value)
60 | {
61 | if (!empty($key) && !empty($value)) {
62 | $this->fields->$key = $value;
63 | }
64 | }
65 |
66 | public function setSingleItem($name, $amount, $id = null)
67 | {
68 | if (!empty($name)) {
69 | $this->fields->item_name = $name;
70 | }
71 |
72 | if (!empty($amount)) {
73 | $this->fields->amount = $amount;
74 | }
75 |
76 | if (!empty($id)) {
77 | $this->fields->item_number = $id;
78 | }
79 | }
80 |
81 | public function setOrderNumber($orderNumber)
82 | {
83 | if (!empty($orderNumber)) {
84 | $this->fields->order_number = $orderNumber;
85 | }
86 | }
87 |
88 | public function setTestMode($mode)
89 | {
90 | if ($mode) {
91 | $this->gatewayUrl = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
92 | $this->testMode = true;
93 | } else {
94 | $this->gatewayUrl = 'https://www.paypal.com/cgi-bin/webscr';
95 | $this->testMode = false;
96 | }
97 | }
98 |
99 | protected function validate()
100 | {
101 | $requiredFields = array(
102 | 'business',
103 | 'currency_code',
104 | 'return',
105 | 'cancel_return',
106 | 'notify_url',
107 | 'item_name',
108 | 'amount'
109 | );
110 |
111 | foreach ($requiredFields as $field) {
112 | if (!isset($this->fields->$field)) {
113 | throw new \Exception("Required parameter {$field} has not been set.");
114 | }
115 | }
116 |
117 | return true;
118 | }
119 |
120 | public function verify()
121 | {
122 | $this->fields->cmd = '_notify-validate';
123 |
124 | $this->httpClient->setUrl($this->gatewayUrl);
125 | $this->httpClient->setPort(80);
126 | $this->httpClient->setMethod("POST");
127 | $this->httpClient->setData($this->fields->getAll());
128 |
129 | $response = $this->httpClient->sendRequest();
130 |
131 | if (strpos($response, "VERIFIED") !== false) {
132 | return true;
133 | }
134 |
135 | return false;
136 | }
137 | }
--------------------------------------------------------------------------------
/src/php/Rbs/Payment/Gateway/AbstractGateway.php:
--------------------------------------------------------------------------------
1 | setDefaults();
17 | $this->init();
18 | }
19 |
20 | private function setDefaults()
21 | {
22 | $this->fields = new \Rbs\Payment\Fields();
23 |
24 | $this->placeholders = array(
25 | 'paymentForm' => '',
26 | 'pageTitle' => 'Proceeding to payment website...',
27 | 'redirectButton' => "Click here",
28 | 'primaryMessage' => "Please wait, your order is being processed and you will be taken to the payment website.",
29 | 'redirectMessage' => "If you are not automatically redirected to payment website within 5 seconds,
"
30 | );
31 |
32 | $this->setTemplateLoader(new \Rbs\Payment\Template\Loader\Filesystem(__DIR__ . "/../templates/basic.html"));
33 | }
34 |
35 | public function setTemplateLoader(\Rbs\Payment\Template\LoaderInterface $loader)
36 | {
37 | if ($this->isValidTemplate($loader)) {
38 | $this->templateLoader = $loader;
39 | } else {
40 | throw new \InvalidArgumentException("Provided template loader does not return required placeholders.");
41 | }
42 | }
43 |
44 | private function isValidTemplate(\Rbs\Payment\Template\LoaderInterface $loader)
45 | {
46 | $html = $loader->load();
47 |
48 | if (strpos($html, 'paymentForm') === false) {
49 | return false;
50 | }
51 |
52 | return true;
53 | }
54 |
55 | public function proceed()
56 | {
57 | if ($this->validate()) {
58 | $this->placeholders['paymentForm'] = $this->getPaymentForm();
59 | $parser = new \Rbs\Payment\Template\Parser($this->templateLoader);
60 | echo $parser->parse($this->placeholders);
61 | }
62 | }
63 |
64 | private function getPaymentForm()
65 | {
66 | $formHtml = '';
67 | $formHtml .= '';
76 |
77 | return $formHtml;
78 | }
79 |
80 | public function populate($fields = array())
81 | {
82 | foreach ($fields as $key => $value) {
83 | $this->fields->$key = $value;
84 | }
85 | }
86 |
87 | public function setHttpClient(\Rbs\Payment\Http\Client $client)
88 | {
89 | $this->httpClient = $client;
90 | }
91 |
92 | abstract protected function init();
93 | abstract protected function validate();
94 |
95 | abstract public function setCurrency($currency);
96 | abstract public function setAccountIdentifier($identifier);
97 | abstract public function setReturnOnSuccessUrl($url);
98 | abstract public function setReturnOnFailureUrl($url);
99 | abstract public function setNotificationUrl($url);
100 | abstract public function setCustomField($key, $value);
101 | abstract public function setSingleItem($name, $amount, $id = null);
102 | abstract public function setOrderNumber($orderNumber);
103 | abstract public function setTestMode($mode);
104 | abstract public function verify();
105 | }
--------------------------------------------------------------------------------
/src/tests/unit-tests/Rbs/Payment/Gateway/PaypalTest.php:
--------------------------------------------------------------------------------
1 | paypal = new \Rbs\Payment\Gateway\Paypal();
15 | }
16 |
17 | public function testCanInitialize()
18 | {
19 | $this->assertInstanceOf("Rbs\\Payment\\Gateway\\AbstractGateway", $this->paypal);
20 | }
21 |
22 | /**
23 | * @expectedException Exception
24 | */
25 | public function testEnsuresMinimumConfiguration()
26 | {
27 | $this->paypal->proceed();
28 | }
29 |
30 | public function testProceedsToPaypalOnSingleItemPurchase()
31 | {
32 | $this->setupSingleItem();
33 |
34 | ob_start();
35 | $this->paypal->proceed();
36 | $output = ob_get_clean();
37 |
38 | $this->assertContains('