├── src ├── logo.gif ├── logo.png ├── img │ ├── logo.png │ ├── undo.png │ ├── trash.png │ ├── ajax-loader.gif │ ├── secure-icon.png │ ├── credit-cards.png │ └── index.php ├── config.xml ├── README.md ├── lib │ ├── index.php │ ├── Simplify │ │ ├── index.php │ │ ├── ResourceList.php │ │ ├── Constants.php │ │ ├── Object.php │ │ ├── Authentication.php │ │ ├── Payment.php │ │ ├── Exceptions.php │ │ ├── PaymentsApi.php │ │ ├── Customer.php │ │ └── Http.php │ └── Simplify.php ├── css │ ├── index.php │ └── style.css ├── js │ ├── index.php │ ├── simplify.form.js │ └── simplify.js ├── views │ ├── index.php │ └── templates │ │ ├── index.php │ │ └── hook │ │ ├── index.php │ │ ├── order-confirmation.tpl │ │ └── payment.tpl ├── validation.php └── simplifycommerce.php ├── README.md └── licence.txt /src/logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddii/simplify-prestashop-module/master/src/logo.gif -------------------------------------------------------------------------------- /src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddii/simplify-prestashop-module/master/src/logo.png -------------------------------------------------------------------------------- /src/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddii/simplify-prestashop-module/master/src/img/logo.png -------------------------------------------------------------------------------- /src/img/undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddii/simplify-prestashop-module/master/src/img/undo.png -------------------------------------------------------------------------------- /src/img/trash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddii/simplify-prestashop-module/master/src/img/trash.png -------------------------------------------------------------------------------- /src/img/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddii/simplify-prestashop-module/master/src/img/ajax-loader.gif -------------------------------------------------------------------------------- /src/img/secure-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddii/simplify-prestashop-module/master/src/img/secure-icon.png -------------------------------------------------------------------------------- /src/img/credit-cards.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddii/simplify-prestashop-module/master/src/img/credit-cards.png -------------------------------------------------------------------------------- /src/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | simplifycommerce 4 | 5 | 6 | 7 | 8 | 9 | Warning: Are you sure you want to uninstall this module? 10 | 1 11 | 0 12 | us 13 | 14 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Simplify Commerce payment module for PrestaShop 3 | 4 | This plugin adds Simplify Commerce as a payment option on your PrestaShop checkout page. 5 | 6 | ## Installation 7 | 1/ If you haven't done so already, download a release (simplifycommerce.zip) from the menu option above and install the zip file through the PrestaShop admin panel and enable it. 8 | 9 | 2/ Configure the module by adding your API keys on the settings page (Click the 'Configure' link on the module) 10 | 11 | Optionally: 12 | 13 | 1/ Unzip the downloaded source zip file and build your own module zip file by executing an 'ant' command within the root directory. 14 | 15 | 2/ You can install the newly built module (located in the 'dist' folder) as before. 16 | 17 | You can manage your Simplify account (view deposits, perform refunds etc.) using the Simplify dashboard at https://www.simplify.com/commerce/. 18 | 19 | ## License 20 | This software is Open Source, released under the BSD 3-Clause license. See licence.txt for more info. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simplify Commerce payment module for PrestaShop # 2 | 3 | This plugin adds Simplify Commerce as a payment option on your PrestaShop checkout page. 4 | 5 | ##### Note: As of PrestaShop v1.6, Simplify Commerce is pre-bundled with PrestaShop's core installation. Please head over to this repository for the latest & greatest on Simplify Commerce module source code - https://github.com/PrestaShop/simplifycommerce ##### 6 | 7 | ## Installation 8 | 1. If you haven't done so already, download a release and install the zip file through the PrestaShop admin panel and enable it. 9 | 10 | 2. Configure the module by adding your API keys on the settings page (Click the 'Configure' link on the module) 11 | 12 | Optionally: 13 | 14 | 1. Unzip the downloaded source zip file and build your own module zip file by executing an 'ant' command within the root directory. 15 | 16 | 2. You can install the newly built module (located in the 'dist' folder) as before. 17 | 18 | You can manage your Simplify account (view deposits, perform refunds etc.) using the Simplify dashboard at https://www.simplify.com/commerce/. 19 | 20 | ## License 21 | This software is Open Source, released under the BSD 3-Clause license. See licence.txt for more info. 22 | -------------------------------------------------------------------------------- /licence.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, MasterCard International Incorporated All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the MasterCard International Incorporated nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /src/lib/index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/css/index.php: -------------------------------------------------------------------------------- 1 | 33 | -------------------------------------------------------------------------------- /src/img/index.php: -------------------------------------------------------------------------------- 1 | 33 | -------------------------------------------------------------------------------- /src/js/index.php: -------------------------------------------------------------------------------- 1 | 33 | -------------------------------------------------------------------------------- /src/lib/Simplify/index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/index.php: -------------------------------------------------------------------------------- 1 | 33 | -------------------------------------------------------------------------------- /src/views/templates/index.php: -------------------------------------------------------------------------------- 1 | 33 | -------------------------------------------------------------------------------- /src/views/templates/hook/index.php: -------------------------------------------------------------------------------- 1 | 33 | -------------------------------------------------------------------------------- /src/validation.php: -------------------------------------------------------------------------------- 1 | processPayment(); 41 | -------------------------------------------------------------------------------- /src/lib/Simplify/ResourceList.php: -------------------------------------------------------------------------------- 1 | () methods. 34 | */ 35 | class SimplifyResourceList { 36 | 37 | /** 38 | * @var array $list the list of domain objects. 39 | */ 40 | public $list = array(); 41 | 42 | /** 43 | * @var int $total the total number of object available. 44 | */ 45 | public $total = 0; 46 | } -------------------------------------------------------------------------------- /src/lib/Simplify/Constants.php: -------------------------------------------------------------------------------- 1 | 59 | -------------------------------------------------------------------------------- /src/views/templates/hook/order-confirmation.tpl: -------------------------------------------------------------------------------- 1 | {* 2 | * Simplify Commerce module to start accepting payments now. It's that simple. 3 | * 4 | * Redistribution and use in source and binary forms, with or without modification, are 5 | * permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright notice, this list of 7 | * conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of 9 | * conditions and the following disclaimer in the documentation and/or other materials 10 | * provided with the distribution. 11 | * Neither the name of the MasterCard International Incorporated nor the names of its 12 | * contributors may be used to endorse or promote products derived from this software 13 | * without specific prior written permission. 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 15 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 17 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 19 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 21 | * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 22 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | * 25 | * @author MasterCard (support@simplify.com) 26 | * @version Release: 1.0.2 27 | * @copyright 2014, MasterCard International Incorporated. All rights reserved. 28 | * @license See licence.txt 29 | *} 30 | {if $simplify_order.valid == 1} 31 |
{l s='Congratulations, your payment has been approved and your order has been saved under the reference' mod='simplifycommerce'} {$simplify_order.reference|escape:html:'UTF-8'}.
32 | {else} 33 |
{l s='Sorry, unfortunately an error occured during the transaction.' mod='simplifycommerce'}

34 | {l s='Please double-check your credit card details and try again or feel free to contact us to resolve this issue.' mod='simplifycommerce'}

35 | ({l s='Your Order\'s Reference:' mod='simplifycommerce'} {$simplify_order.reference|escape:html:'UTF-8'})
36 | {/if} 37 | -------------------------------------------------------------------------------- /src/lib/Simplify/Object.php: -------------------------------------------------------------------------------- 1 | properties)) 45 | return $this->properties[$key]; 46 | else 47 | return null; 48 | } 49 | 50 | /** 51 | * @ignore 52 | */ 53 | public function __set($key, $value) 54 | { 55 | $this->properties[$key] = $value; 56 | } 57 | 58 | /** 59 | * Updates the object's properties with the values in the specified map. 60 | * @param $hash array Map of values to set. 61 | */ 62 | public function setAll($hash) 63 | { 64 | foreach ($hash as $key => $value) 65 | $this->$key = $value; 66 | } 67 | 68 | /** 69 | * @ignore 70 | */ 71 | public function __toString() 72 | { 73 | return Tools::jsonEncode($this->properties); 74 | } 75 | 76 | /** 77 | * Returns the object's properties as a map. 78 | * @return array map of properties. 79 | */ 80 | public function getProperties() 81 | { 82 | return $this->properties; 83 | } 84 | } -------------------------------------------------------------------------------- /src/lib/Simplify.php: -------------------------------------------------------------------------------- 1 | $public_key - this is your API public key 36 | *

$private_key - this is your API private key 37 | *

$access_token - Oauth access token that is needed to make API requests on behalf of another user 38 | *

39 | *

40 | * new SimplifyAuthentication($access_token) 41 | * 42 | *

43 | * new SimplifyAuthentication($public_key, $private_key) 44 | * 45 | *

46 | * new SimplifyAuthentication($public_key, $private_key, $access_token) 47 | */ 48 | class SimplifyAuthentication { 49 | 50 | public $private_key; 51 | public $public_key; 52 | public $access_token; 53 | 54 | public function __construct() 55 | { 56 | $args = func_get_args(); 57 | switch (func_num_args()) 58 | { 59 | case 1: 60 | self::construct1( $args[0] ); 61 | break; 62 | case 2: 63 | self::construct2( $args[0], $args[1] ); 64 | break; 65 | case 3: 66 | self::construct3( $args[0], $args[1], $args[2] ); 67 | } 68 | } 69 | 70 | private function construct1($access_token) 71 | { 72 | $this->access_token = $access_token; 73 | } 74 | 75 | private function construct2($public_key, $private_key) 76 | { 77 | $this->public_key = $public_key; 78 | $this->private_key = $private_key; 79 | } 80 | 81 | private function construct3($public_key, $private_key, $access_token) 82 | { 83 | $this->public_key = $public_key; 84 | $this->private_key = $private_key; 85 | $this->access_token = $access_token; 86 | } 87 | } -------------------------------------------------------------------------------- /src/lib/Simplify/Payment.php: -------------------------------------------------------------------------------- 1 | 36 | *

amount
Amount of the payment (minor units). Example: 1000 = 10.00 required
37 | *
card.addressCity
City of the cardholder.
38 | *
card.addressCountry
Country code (ISO-3166-1-alpha-2 code) of residence of the cardholder.
39 | *
card.addressLine1
Address of the cardholder.
40 | *
card.addressLine2
Address of the cardholder if needed.
41 | *
card.addressState
State code (USPS code) of residence of the cardholder.
42 | *
card.addressZip
Postal code of the cardholder.
43 | *
card.cvc
CVC security code of the card. This is the code on the back of the card. Example: 123
44 | *
card.expMonth
Expiration month of the card. Format is MM. Example: January = 01 required
45 | *
card.expYear
Expiration year of the card. Format is YY. Example: 2013 = 13 required
46 | *
card.name
Name as it appears on the card.
47 | *
card.number
Card number as it appears on the card. required
48 | *
currency
Currency code (ISO-4217) for the transaction. Must match the currency associated with your account. 49 | * required default:USD
50 | *
customer
ID of customer. If specified, card on file of customer will be used.
51 | *
description
Custom naming of payment for external systems to use.
52 | *
reference
Custom reference field to be used with outside systems.
53 | *
token
If specified, card associated with card token will be used.
54 | * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and 55 | * Simplify::private_key are used. For backwards compatibility the public and 56 | * private keys may be passed instead of the authentication object. 57 | * @return Payment a Payment object. 58 | */ 59 | public static function createPayment($hash, $authentication = null) 60 | { 61 | $args = func_get_args(); 62 | $authentication = SimplifyPaymentsApi::buildAuthenticationObject($authentication, $args, 2); 63 | 64 | $instance = new SimplifyPayment(); 65 | $instance->setAll($hash); 66 | 67 | $object = SimplifyPaymentsApi::createObject($instance, $authentication); 68 | return $object; 69 | } 70 | 71 | 72 | 73 | /** 74 | * Retrieve SimplifyPayment objects. 75 | * @param array criteria a map of parameters; valid keys are:
76 | *
filter
Filters to apply to the list.
77 | *
max
Allows up to a max of 50 list items to return. default:20
78 | *
offset
Used in paging of the list. This is the start offset of the page. default:0
79 | *
sorting
Allows for ascending or descending sorting of the list. The value maps properties to the sort direction 80 | * (either asc for ascending or desc for descending). Sortable properties are: dateCreated amount 81 | * id description paymentDate.
82 | * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and 83 | * Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. 84 | * @return ResourceList a ResourceList object that holds the list of Payment objects and the total 85 | * number of Payment objects available for the given criteria. 86 | * @see ResourceList 87 | */ 88 | public static function listPayment($criteria = null, $authentication = null) 89 | { 90 | $args = func_get_args(); 91 | $authentication = SimplifyPaymentsApi::buildAuthenticationObject($authentication, $args, 2); 92 | 93 | $val = new SimplifyPayment(); 94 | $list = SimplifyPaymentsApi::listObject($val, $criteria, $authentication); 95 | 96 | return $list; 97 | } 98 | 99 | 100 | /** 101 | * Retrieve a SimplifyPayment object from the API 102 | * 103 | * @param string id the id of the Payment object to retrieve 104 | * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and 105 | * Simplify::private_key are used. For backwards compatibility the public and 106 | * private keys may be passed instead of the authentication object. 107 | * @return Payment a Payment object 108 | */ 109 | public static function findPayment($id, $authentication = null) 110 | { 111 | $args = func_get_args(); 112 | $authentication = SimplifyPaymentsApi::buildAuthenticationObject($authentication, $args, 2); 113 | 114 | $val = new SimplifyPayment(); 115 | $val->id = $id; 116 | 117 | $obj = SimplifyPaymentsApi::findObject($val, $authentication); 118 | 119 | return $obj; 120 | } 121 | 122 | /** 123 | * @ignore 124 | */ 125 | public function getClazz() 126 | { 127 | return 'Payment'; 128 | } 129 | } -------------------------------------------------------------------------------- /src/lib/Simplify/Exceptions.php: -------------------------------------------------------------------------------- 1 | status = $status; 51 | $this->error_code = null; 52 | $this->reference = null; 53 | 54 | if ($error_data != null) 55 | { 56 | $this->reference = $error_data['reference']; 57 | $this->error_data = $error_data; 58 | 59 | $error = $error_data['error']; 60 | if ($error != null) 61 | { 62 | $m = $error['message']; 63 | if ($m != null) 64 | $this->message = $m; 65 | 66 | $this->error_code = $error['code']; 67 | } 68 | } 69 | } 70 | 71 | /** 72 | * Returns a map of all error data returned by the API. 73 | * @return array a map containing API error data. 74 | */ 75 | public function getErrorData() 76 | { 77 | return $this->error_data; 78 | } 79 | 80 | /** 81 | * Returns the HTTP status for the request. 82 | * @return string HTTP status code (or null if there is no status). 83 | */ 84 | public function getStatus() 85 | { 86 | return $this->status; 87 | } 88 | 89 | /** 90 | * Returns unique reference for the API error. 91 | * @return string a reference (or null if there is no reference). 92 | */ 93 | public function getReference() 94 | { 95 | return $this->reference; 96 | } 97 | 98 | /** 99 | * Returns an code for the API error. 100 | * @return string the error code. 101 | */ 102 | public function getErrorCode() 103 | { 104 | return $this->error_code; 105 | } 106 | 107 | /** 108 | * Returns a description of the error. 109 | * @return string Description of the error. 110 | */ 111 | public function describe() 112 | { 113 | return get_class($this).': \'' 114 | .$this->getMessage().'\' (status: ' 115 | .$this->getStatus().', error code: ' 116 | .$this->getErrorCode().', reference: ' 117 | .$this->getReference().')'; 118 | } 119 | 120 | } 121 | 122 | 123 | /** 124 | * Exception raised when there are communication problems contacting the API. 125 | */ 126 | class SimplifyApiConnectionException extends SimplifyApiException { 127 | 128 | } 129 | 130 | /** 131 | * Exception raised where there are problems authenticating a request. 132 | */ 133 | class SimplifyAuthenticationException extends SimplifyApiException { 134 | 135 | } 136 | 137 | /** 138 | * Exception raised when the API request contains errors. 139 | */ 140 | class SimplifyBadRequestException extends SimplifyApiException { 141 | 142 | protected $field_errors; 143 | 144 | /** 145 | * @ignore 146 | */ 147 | public function __construct($message, $status = null, $error_data = null) 148 | { 149 | parent::__construct($message, $status, $error_data); 150 | 151 | $field_errors = array(); 152 | 153 | if ($error_data != null) 154 | { 155 | $error = $error_data['error']; 156 | if ($error != null) 157 | { 158 | $field_errors = $error['fieldErrors']; 159 | if ($field_errors != null) 160 | { 161 | $this->field_errors = array(); 162 | foreach ($field_errors as $field_error) 163 | array_push($this->field_errors, new SimplifyFieldError($field_error)); 164 | } 165 | } 166 | } 167 | } 168 | 169 | /** 170 | * Returns a boolean indicating whether there are any field errors. 171 | * @return boolean true if there are field errors; false otherwise. 172 | */ 173 | public function hasFieldErrors() 174 | { 175 | return count($this->field_errors) > 0; 176 | } 177 | 178 | /** 179 | * Returns a list containing all field errors. 180 | * @return array list of field errors. 181 | */ 182 | public function getFieldErrors() 183 | { 184 | return $this->field_errors; 185 | } 186 | 187 | /** 188 | * Returns a description of the error. 189 | * @return string description of the error. 190 | */ 191 | public function describe() 192 | { 193 | $s = parent::describe(); 194 | foreach ($this->getFieldErrors() as $field_error) 195 | $s = $s.'\n'.(string)$field_error; 196 | 197 | return $s.'\n'; 198 | } 199 | 200 | } 201 | 202 | /** 203 | * Represents a single error in a field of a request sent to the API. 204 | */ 205 | class SimplifyFieldError { 206 | 207 | protected $field; 208 | protected $code; 209 | protected $message; 210 | 211 | /** 212 | * @ignore 213 | */ 214 | public function __construct($error_data) 215 | { 216 | $this->field = $error_data['field']; 217 | $this->code = $error_data['code']; 218 | $this->message = $error_data['message']; 219 | } 220 | 221 | /** 222 | * Returns the name of the field with the error. 223 | * @return string the field name. 224 | */ 225 | public function getFieldName() 226 | { 227 | return $this->field; 228 | } 229 | 230 | /** 231 | * Returns the code for the error. 232 | * @return string the error code. 233 | */ 234 | public function getErrorCode() 235 | { 236 | return $this->code; 237 | } 238 | 239 | /** 240 | * Returns a description of the error. 241 | * @return string description of the error. 242 | */ 243 | public function getMessage() 244 | { 245 | return $this->message; 246 | } 247 | 248 | public function __toString() 249 | { 250 | return 'Field error: '.$this->getFieldName().'\''.$this->getMessage().'\' ('.$this->getErrorCode().')'; 251 | } 252 | 253 | } 254 | 255 | /** 256 | * Exception when a requested object cannot be found. 257 | */ 258 | class SimplifyObjectNotFoundException extends SimplifyApiException { 259 | 260 | } 261 | 262 | /** 263 | * Exception when a request was not allowed. 264 | */ 265 | class SimplifyNotAllowedException extends SimplifyApiException { 266 | 267 | } 268 | 269 | /** 270 | * Exception when there was a system error processing a request. 271 | */ 272 | class SimplifySystemException extends SimplifyApiException { 273 | 274 | } 275 | -------------------------------------------------------------------------------- /src/views/templates/hook/payment.tpl: -------------------------------------------------------------------------------- 1 | {* 2 | * Simplify Commerce module to start accepting payments now. It's that simple. 3 | * 4 | * Redistribution and use in source and binary forms, with or without modification, are 5 | * permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright notice, this list of 7 | * conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of 9 | * conditions and the following disclaimer in the documentation and/or other materials 10 | * provided with the distribution. 11 | * Neither the name of the MasterCard International Incorporated nor the names of its 12 | * contributors may be used to endorse or promote products derived from this software 13 | * without specific prior written permission. 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 15 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 17 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 19 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 20 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 21 | * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 22 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | * SUCH DAMAGE. 24 | * 25 | * @author MasterCard (support@simplify.com) 26 | * @version Release: 1.0.2 27 | * @copyright 2014, MasterCard International Incorporated. All rights reserved. 28 | * @license See licence.txt 29 | *} 30 | 34 |
35 |
36 |

Pay by Credit Card

37 | Secure Icon 38 |
39 | ( TEST PAYMENT ) 40 | 41 |
42 |
43 | 44 |
45 | Your payment is being processed... 46 | Loader Icon 47 |
48 |
49 | {if isset($show_saved_card_details)} 50 |
51 |
52 |
 
53 | 54 |
55 |
56 |
Card Type
57 |
{$customer_details->card->type|escape:'htmlall'}
58 |
59 |
60 |
Card Number
61 |
xxxx - xxxx - xxxx - {$customer_details->card->last4|escape:'htmlall'}
62 |
63 |
64 |
Expiry Date
65 |
66 | {$customer_details-> card->expMonth|escape:'htmlall'} / {$customer_details->card->expYear|escape:'htmlall'} 67 |
68 |
69 | trash icon 70 |
71 |
72 |
Delete Credit Card?
73 |
74 | Yes 75 | No 76 |
77 |
78 |
79 | 80 |
81 |
82 |
83 |
Your credit card has been deleted: Undo Secure Icon
84 | {/if} 85 |
86 | {if isset($show_saved_card_details)} 87 |
88 |
89 | 90 |
91 |
92 |
New Credit Card
93 |
94 |
95 | {/if} 96 | 130 |
131 | 134 | 135 | 138 |
139 |
140 | -------------------------------------------------------------------------------- /src/js/simplify.form.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2014, MasterCard International Incorporated 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, are 6 | * permitted provided that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this list of 9 | * conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, this list of 11 | * conditions and the following disclaimer in the documentation and/or other materials 12 | * provided with the distribution. 13 | * Neither the name of the MasterCard International Incorporated nor the names of its 14 | * contributors may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 23 | * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | /** 29 | * JQuery validations 30 | */ 31 | (function() { 32 | 33 | isNumeric = function(num) { 34 | return /[\d\s]/.test(num) 35 | }; 36 | 37 | isTextSelected = function(input) { 38 | if (typeof input.prop('selectionStart') == "number") { 39 | return input.prop('selectionStart') != input.prop('selectionEnd'); 40 | } else if (typeof document.selection != "undefined") { 41 | input.focus(); 42 | return document.selection.createRange().text == input.val(); 43 | } 44 | }; 45 | 46 | cardType = function(num) { 47 | 48 | var MASTERCARD = '51,52,53,54,55,'; 49 | var VISA = '4'; 50 | var AMEX = '34,37,'; 51 | var DISCOVER = '60,62,64,65,'; 52 | 53 | if (!num) return null; 54 | 55 | if (num.substring(0, 1) === VISA) { 56 | return 'visa'; 57 | } 58 | var prefix = num.substring(0, 2) + ',' 59 | if (prefix.length != 3) return null; 60 | 61 | if (MASTERCARD.indexOf(prefix) != -1) { 62 | return 'mastercard'; 63 | } 64 | if (AMEX.indexOf(prefix) != -1) { 65 | return 'amex'; 66 | } 67 | if (DISCOVER.indexOf(prefix) != -1) { 68 | return 'discover'; 69 | } 70 | return null; 71 | }; 72 | 73 | restrictNumeric = function(e) { 74 | 75 | var keyCode = e.which 76 | if (keyCode === 32) { 77 | return false; 78 | } 79 | // allow some special characters, backspace etc 80 | if (keyCode < 33) { 81 | return true; 82 | } 83 | 84 | var keyChar = String.fromCharCode(keyCode); 85 | return isNumeric(keyChar); 86 | }; 87 | 88 | maxlength = function(e) { 89 | 90 | var input = $(this); 91 | 92 | if (isTextSelected(input)) return; 93 | 94 | var type = input.cardType(); 95 | var keyChar = String.fromCharCode(e.which); 96 | 97 | if (!isNumeric(keyChar)) { 98 | return; 99 | } 100 | 101 | var value = input.val() + keyChar; 102 | value = value.replace(/\D/g, ''); 103 | 104 | if (type == 'amex') { 105 | return value.length <= 15; 106 | } else { 107 | return value.length <= 16; 108 | } 109 | }; 110 | 111 | formatCardInput = function(e) { 112 | 113 | var input = $(this); 114 | 115 | if (isTextSelected(input)) return; 116 | 117 | var type = input.cardType(); 118 | var value = input.val(); 119 | var keyChar = String.fromCharCode(e.which); 120 | 121 | if (!isNumeric(keyChar)) { 122 | return; 123 | } 124 | 125 | var maxlength = 16; 126 | var pattern = /(?:^|\s)(\d{4})$/; 127 | 128 | if (type === 'amex') { 129 | maxlength = 15; 130 | pattern = /^(\d{4}|\d{4}\s\d{6})$/; 131 | } 132 | 133 | var length = (value.replace(/\D/g, '') + keyChar).length; 134 | if (length >= maxlength) { 135 | return; 136 | } 137 | 138 | if (pattern.test(value)) { 139 | e.preventDefault(); 140 | return input.val(value + ' ' + keyChar); 141 | } else if (pattern.test(value + keyChar)) { 142 | e.preventDefault(); 143 | return input.val(value + keyChar + ' '); 144 | } 145 | }; 146 | 147 | formatCardBackspace = function(e) { 148 | 149 | var BACK_SPACE = 8; 150 | 151 | var input = $(this); 152 | var value = input.val(); 153 | 154 | if (isTextSelected(input)) return; 155 | 156 | if (e.which === BACK_SPACE && /\s\d?$/.test(value)) { 157 | e.preventDefault(); 158 | return input.val(value.replace(/\s\d?$/, '')); 159 | } 160 | }; 161 | 162 | formatExpiryInput = function(e) { 163 | 164 | var input = $(this); 165 | var value = $(this).val(); 166 | 167 | if (isTextSelected(input)) return; 168 | 169 | var keyChar = String.fromCharCode(e.which); 170 | var slash = keyChar == '/'; 171 | 172 | if (value.replace(/\D/g, '').length >= 4 && e.which != 8 && e.which != 0) { 173 | return false; 174 | } 175 | 176 | if (!isNumeric(keyChar) && !slash) { 177 | return; 178 | } 179 | 180 | if (value.length != 1 && slash) { 181 | return; 182 | } 183 | 184 | if (value.length == 1 && slash) { 185 | input.val('0' + value + '/'); 186 | } else if (value.length == 1 && !slash) { 187 | input.val(value + keyChar + '/'); 188 | } else { 189 | input.val(value + keyChar); 190 | } 191 | 192 | e.preventDefault(); 193 | }; 194 | 195 | expiryDate = function(expiry) { 196 | 197 | if (expiry) { 198 | 199 | var dates = expiry.split('/') 200 | return { 201 | 'month': dates[0], 202 | 'year': dates[1] 203 | } 204 | } 205 | return { 206 | 'month': null, 207 | 'year': null 208 | } 209 | }; 210 | 211 | /** 212 | * Restrict the entry of a non numeric in an input field 213 | */ 214 | $.fn.restrictNumeric = function() { 215 | return this.keypress(restrictNumeric); 216 | }; 217 | 218 | $.fn.cardType = function() { 219 | return cardType(this.val()); 220 | }; 221 | 222 | /** 223 | * Formats the entry of a card number into groups of four e.g. **** **** **** **** 224 | * @returns {*} 225 | */ 226 | $.fn.formatCardNumber = function() { 227 | this.restrictNumeric(); 228 | this.keypress(maxlength); 229 | this.keypress(formatCardInput); 230 | this.keydown(formatCardBackspace); 231 | return 232 | }; 233 | 234 | /** 235 | * Formats an expiry date into dd/dd 236 | */ 237 | $.fn.formatExpiryNumber = function() { 238 | this.restrictNumeric(); 239 | this.keypress(formatExpiryInput); 240 | return 241 | }; 242 | 243 | /** 244 | * Return an object with the expiry month and year 245 | * 246 | * {'month': '01', 'year': '12'} 247 | * 248 | * @returns {*} 249 | */ 250 | $.fn.expiryDate = function() { 251 | return expiryDate(this.val()); 252 | }; 253 | 254 | }).call(this); -------------------------------------------------------------------------------- /src/js/simplify.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2014, MasterCard International Incorporated 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, are 6 | * permitted provided that the following conditions are met: 7 | * 8 | * Redistributions of source code must retain the above copyright notice, this list of 9 | * conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, this list of 11 | * conditions and the following disclaimer in the documentation and/or other materials 12 | * provided with the distribution. 13 | * Neither the name of the MasterCard International Incorporated nor the names of its 14 | * contributors may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 | * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 23 | * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 | * SUCH DAMAGE. 26 | */ 27 | 28 | /** 29 | * Function to handle the form submission 30 | */ 31 | $(document).ready(function() { 32 | 33 | if ($('.simplify-payment-errors').text().length > 0) { 34 | $('.simplify-payment-errors').show(); 35 | } 36 | 37 | // Check that the Simplify API Keys are set 38 | if (simplifyPublicKey.length == 0) { 39 | $('#simplify-no-keys-msg').show(); 40 | $('.simplify-submit-button').attr('disabled', 'disabled'); 41 | return; 42 | } 43 | 44 | // Display warning message that this is a test payment as test api keys are being used. 45 | if (simplifyPublicKey.indexOf('sbpb_') !== -1) { 46 | $('#simplify-test-mode-msg').show(); 47 | } 48 | 49 | $(".simplify-card-cvc").restrictNumeric(); 50 | $('.simplify-card-number').formatCardNumber(); 51 | 52 | /** 53 | * Function to watch the form of payment being used and 54 | * to show and hide the relevant form components. 55 | */ 56 | $("input[name='cc-type']").change(function() { 57 | var ccDetails = $("#simplify-cc-details"); 58 | $('.card-type-container').removeClass('selected'); 59 | $(this).parents('.card-type-container').addClass('selected'); 60 | 61 | if ($("input[name='cc-type']:checked").val() == 'new') { 62 | if ($("#cc-deletion-msg").is(':visible')) { 63 | showSaveCardDetailsLabel(true); 64 | } else { 65 | showSaveCardDetailsLabel(false); 66 | } 67 | 68 | ccDetails.fadeIn(); 69 | } else { 70 | ccDetails.fadeOut(); 71 | } 72 | }); 73 | 74 | /** 75 | * Function to show the confirm deletion container when the 76 | * trash icon is clicked. 77 | */ 78 | $('#trash-icon').click(function() { 79 | $('#cc-confirm-deletion').slideDown(); 80 | }); 81 | 82 | /** 83 | * Function to hide the credit card details option, 84 | * select the 'new card' option and provide the user 85 | * a control to undo the deletion. 86 | */ 87 | $('#confirm-cc-deletion').click(function() { 88 | $("#old-card-container").fadeOut('fast', function() { 89 | $("#new-card-container input[name='cc-type']").click(); 90 | $("#cc-deletion-msg").slideDown(function() { 91 | showSaveCardDetailsLabel(true); 92 | }); 93 | }); 94 | 95 | $('#simplify-payment-form').append(''); 96 | }); 97 | 98 | /** 99 | * Function to hide the confirm deletion container. 100 | */ 101 | $('#cancel-cc-deletion').click(function() { 102 | $('#cc-confirm-deletion').slideUp(); 103 | }); 104 | 105 | /** 106 | * Function to restore the save card details 107 | * form option. 108 | */ 109 | $('#cc-undo-deletion-lnk').click(function() { 110 | $("#cc-deletion-msg").hide(); 111 | $('#cc-confirm-deletion').hide(); 112 | $("#old-card-container").fadeIn('fast'); 113 | $('#deleteCustomerCard').remove(); 114 | showSaveCardDetailsLabel(false); 115 | }); 116 | 117 | /** 118 | * Function to handle the form submission and either 119 | * generate a new card token for new cards or 120 | * charge an existing user's card. 121 | */ 122 | $('#simplify-payment-form').submit(function() { 123 | 124 | $('#simplify-ajax-loader').show(); 125 | $('.simplify-payment-errors').hide(); 126 | $('.simplify-submit-button').attr('disabled', 'disabled'); /* Disable the submit button to prevent repeated clicks */ 127 | 128 | // Fetch a card token for new card details otherwise submit form with existing card details 129 | if ($("#simplify-cc-details").is(':visible')) { 130 | if (simplifyPublicKey.length == 0) { 131 | return false; 132 | } 133 | 134 | SimplifyCommerce.generateToken({ 135 | key: simplifyPublicKey, 136 | card: { 137 | number: $(".simplify-card-number").val().trim().replace(/\s+/g, ''), 138 | cvc: $(".simplify-card-cvc").val(), 139 | expMonth: $("#simplify-cc-details select[name='Date_Month']").val(), 140 | expYear: $("#simplify-cc-details select[name='Date_Year']").val().substring(2), 141 | name: simplifyFirstname + ' ' + simplifyLastname, 142 | addressCity: simplifyCity, 143 | addressLine1: simplifyAddress1, 144 | addressLine2: simplifyAddress2, 145 | addressState: simplifyState, 146 | addressZip: simplifyPostcode 147 | } 148 | }, simplifyResponseHandler); 149 | 150 | return false; /* Prevent the form from submitting with the default action */ 151 | } else { 152 | $('#simplify-payment-form') 153 | .append('') 154 | .get(0).submit(); 155 | } 156 | }); 157 | 158 | }); 159 | 160 | /** 161 | * Function to retrieve a cardholder detail or empty string if it doesn't exist. 162 | */ 163 | function getCardHolderDetail(detail) { 164 | return (typeof cardholderDetails[detail] !== 'undefined') ? cardholderDetails[detail] : ''; 165 | } 166 | 167 | /** 168 | * Function to toggle the visibility of the the 'save card details' label 169 | */ 170 | function showSaveCardDetailsLabel(isSaveCardeDetailsLabelVisible) { 171 | var $saveCustomerLabel = $('#saveCustomerLabel'), 172 | $updateCustomerLabel = $('#updateCustomerLabel'); 173 | 174 | if (isSaveCardeDetailsLabelVisible) { 175 | $saveCustomerLabel.show(); 176 | $updateCustomerLabel.hide(); 177 | } else { 178 | $updateCustomerLabel.show(); 179 | $saveCustomerLabel.hide(); 180 | } 181 | } 182 | 183 | /** 184 | * Function to handle the response from Simplify Commerce's tokenization call. 185 | */ 186 | function simplifyResponseHandler(data) { 187 | if (data.error) { 188 | var errorMessages = { 189 | 'card.number': 'The credit card number you entered is invalid.', 190 | 'card.expYear': 'The expiry year on the credit card is invalid.' 191 | }; 192 | 193 | // Show any validation errors 194 | if (data.error.code == "validation") { 195 | var fieldErrors = data.error.fieldErrors, 196 | fieldErrorsLength = fieldErrors.length, 197 | errorList = ""; 198 | 199 | for (var i = 0; i < fieldErrorsLength; i++) { 200 | errorList += "
" + errorMessages[fieldErrors[i].field] + 201 | " " + fieldErrors[i].message + ".
"; 202 | } 203 | // Display the errors 204 | $('.simplify-payment-errors') 205 | .html(errorList) 206 | .show(); 207 | } 208 | // Re-enable the submit button 209 | $('.simplify-submit-button').removeAttr('disabled'); 210 | $('#simplify-payment-form').show(); 211 | $('#simplify-ajax-loader').hide(); 212 | } else { 213 | // Insert the token into the form so it gets submitted to the server 214 | $('#simplify-payment-form') 215 | .append('') 216 | .append('') 217 | .get(0).submit(); 218 | } 219 | } -------------------------------------------------------------------------------- /src/lib/Simplify/PaymentsApi.php: -------------------------------------------------------------------------------- 1 | 'POST', 40 | 'delete' => 'DELETE', 41 | 'list' => 'GET', 42 | 'show' => 'GET', 43 | 'update' => 'PUT' 44 | ); 45 | 46 | /** 47 | * @ignore 48 | */ 49 | public static function createObject($object, $authentication = null) 50 | { 51 | $payments_api = new SimplifyPaymentsApi(); 52 | 53 | $json_object = $payments_api->execute('create', $object, $authentication); 54 | 55 | $o = $payments_api->convertFromHashToObject($json_object, $object->getClazz()); 56 | 57 | return $o; 58 | } 59 | 60 | /** 61 | * @ignore 62 | */ 63 | public static function findObject($object, $authentication = null) 64 | { 65 | $payments_api = new SimplifyPaymentsApi(); 66 | 67 | $json_object = $payments_api->execute('show', $object, $authentication); 68 | $o = $payments_api->convertFromHashToObject($json_object, $object->getClazz()); 69 | 70 | return $o; 71 | } 72 | 73 | /** 74 | * @ignore 75 | */ 76 | public static function updateObject($object, $authentication = null) 77 | { 78 | $payments_api = new SimplifyPaymentsApi(); 79 | 80 | $json_object = $payments_api->execute('update', $object, $authentication); 81 | $o = $payments_api->convertFromHashToObject($json_object, $object->getClazz()); 82 | 83 | return $o; 84 | } 85 | 86 | /** 87 | * @ignore 88 | */ 89 | public static function deleteObject($object, $authentication = null) 90 | { 91 | $payments_api = new SimplifyPaymentsApi(); 92 | 93 | $json_object = $payments_api->execute('delete', $object, $authentication); 94 | 95 | return $json_object; 96 | } 97 | 98 | /** 99 | * @ignore 100 | */ 101 | public static function listObject($object, $criteria = null, $authentication = null) 102 | { 103 | if ($criteria != null) 104 | { 105 | if (isset($criteria['max'])) 106 | $object->max = $criteria['max']; 107 | if (isset($criteria['offset'])) 108 | $object->offset = $criteria['offset']; 109 | if (isset($criteria['sorting'])) 110 | $object->sorting = $criteria['sorting']; 111 | if (isset($criteria['filter'])) 112 | $object->filter = $criteria['filter']; 113 | } 114 | 115 | $payments_api = new SimplifyPaymentsApi(); 116 | $json_object = $payments_api->execute('list', $object, $authentication); 117 | 118 | $ret = new SimplifyResourceList(); 119 | if (array_key_exists('list', $json_object) & is_array($json_object['list'])) 120 | { 121 | foreach ($json_object['list'] as $obj) 122 | array_push($ret->list, $payments_api->convertFromHashToObject($obj, $object->getClazz())); 123 | 124 | $ret->total = $json_object['total']; 125 | } 126 | 127 | return $ret; 128 | } 129 | 130 | /** 131 | * @ignore 132 | */ 133 | public function convertFromHashToObject($from, $to_clazz) 134 | { 135 | $clazz = 'stdClass'; 136 | $to_clazz = 'Simplify'.$to_clazz; 137 | if ('stdClass' != $to_clazz && class_exists("{$to_clazz}", false)) 138 | $clazz = "{$to_clazz}"; 139 | 140 | $object = new $clazz(); 141 | 142 | foreach ($from as $key => $value) 143 | { 144 | if (is_array($value) && count(array_keys($value))) 145 | { 146 | $new_clazz = 'Simplify'.Tools::ucfirst($key); 147 | 148 | if (!class_exists($new_clazz, false)) 149 | $new_clazz = 'stdClass'; 150 | 151 | $object->$key = $this->convertFromHashToObject($value, $new_clazz); 152 | } 153 | else 154 | $object->$key = $value; 155 | } 156 | 157 | return $object; 158 | } 159 | 160 | /** 161 | * @ignore 162 | */ 163 | public function getUrl($public_key, $action, $object) 164 | { 165 | $url = $this->fixUrl(Simplify::$api_base_sandbox_url); 166 | if ($this->isLiveKey($public_key)) 167 | $url = $this->fixUrl(Simplify::$api_base_live_url); 168 | 169 | $url = $this->fixUrl($url).urlencode(lcfirst($object->getClazz())).'/'; 170 | 171 | $query_params = array(); 172 | if ($action == 'show') 173 | $url .= urlencode($object->id); 174 | elseif ($action == 'list') 175 | { 176 | $query_params = array_merge($query_params, array('max' => $object->max, 'offset' => $object->offset)); 177 | if (is_array($object->filter) && count(array_keys($object->filter))) 178 | { 179 | foreach ($object->filter as $key => $value) 180 | $query_params["filter[$key]"] = $value; 181 | } 182 | if (is_array($object->sorting) && count(array_keys($object->sorting))) 183 | { 184 | foreach ($object->sorting as $key => $value) 185 | $query_params["sorting[$key]"] = $value; 186 | } 187 | $query = http_build_query($query_params); 188 | if ($query != '') 189 | { 190 | if (strpos($url, '?', Tools::strlen($url)) === false) $url .= '?'; 191 | $url .= $query; 192 | } 193 | 194 | } 195 | elseif ($action == 'delete') 196 | $url .= urlencode($object->id); 197 | elseif ($action == 'update') 198 | $url .= urlencode($object->id); 199 | 200 | return $url; 201 | } 202 | 203 | /** 204 | * @ignore 205 | */ 206 | public function getMethod($action) 207 | { 208 | if (array_key_exists(Tools::strtolower($action), self::$method_map)) 209 | return self::$method_map[Tools::strtolower($action)]; 210 | 211 | return 'GET'; 212 | } 213 | 214 | /** 215 | * @ignore 216 | */ 217 | private function execute($action, $object, $authentication) 218 | { 219 | $http = new SimplifyHTTP(); 220 | 221 | return $http->apiRequest($this->getUrl($authentication->public_key, $action, $object), $this->getMethod($action), 222 | $authentication, Tools::jsonEncode($object->getProperties())); 223 | } 224 | 225 | /** 226 | * @ignore 227 | */ 228 | public function jwsDecode($hash, $authentication) 229 | { 230 | $http = new SimplifyHTTP(); 231 | 232 | $data = $http->jwsDecode($authentication, $hash); 233 | 234 | return Tools::jsonDecode($data, true); 235 | } 236 | 237 | /** 238 | * @ignore 239 | */ 240 | private function fixUrl($url) 241 | { 242 | if ($this->endsWith($url, '/')) 243 | return $url; 244 | 245 | return $url.'/'; 246 | } 247 | 248 | /** 249 | * @ignore 250 | */ 251 | private function isLiveKey($k) 252 | { 253 | return strpos($k, 'lvpb') === 0; 254 | } 255 | 256 | /** 257 | * @ignore 258 | */ 259 | private function endsWith($s, $c) 260 | { 261 | return Tools::substr($s, -Tools::strlen($c)) == $c; 262 | } 263 | 264 | /** 265 | * Helper function to build the Authentication object for backwards compatibility. 266 | * An array of all the arguments passed to one of the API functions is checked against what 267 | * we expect to received. If it's greater, then we're assuming that the user is using the older way of 268 | * passing the keys. i.e as two separate strings. We take those two string and create the Authentication object 269 | * 270 | * @ignore 271 | * @param $authentication 272 | * @param $args 273 | * @param $expected_arg_count 274 | * @return SimplifyAuthentication 275 | */ 276 | public static function buildAuthenticationObject($authentication = null, $args, $expected_arg_count) 277 | { 278 | if (count($args) > $expected_arg_count) 279 | $authentication = new SimplifyAuthentication($args[$expected_arg_count - 1], $args[$expected_arg_count]); 280 | 281 | if ($authentication == null) 282 | $authentication = new SimplifyAuthentication(); 283 | 284 | // check that the keys have been set, if not use the global keys 285 | if (empty($authentication->public_key)) 286 | $authentication->public_key = Simplify::$public_key; 287 | 288 | if (empty($authentication->private_key)) 289 | $authentication->private_key = Simplify::$private_key; 290 | 291 | return $authentication; 292 | } 293 | 294 | } 295 | 296 | -------------------------------------------------------------------------------- /src/lib/Simplify/Customer.php: -------------------------------------------------------------------------------- 1 | 36 | *
card.addressCity
City of the cardholder. required
37 | *
card.addressCountry
Country code (ISO-3166-1-alpha-2 code) of residence of the cardholder. 38 | * required
39 | *
card.addressLine1
Address of the cardholder required
40 | *
card.addressLine2
Address of the cardholder if needed. required
41 | *
card.addressState
State code (USPS code) of residence of the cardholder. required
42 | *
card.addressZip
Postal code of the cardholder. required
43 | *
card.cvc
CVC security code of the card. This is the code on the back of the card. Example: 123 44 | * required
45 | *
card.expMonth
Expiration month of the card. Format is MM. Example: January = 01 required
46 | *
card.expYear
Expiration year of the card. Format is YY. Example: 2013 = 13 required
47 | *
card.id
ID of card. Unused during customer create.
48 | *
card.name
Name as appears on the card. required
49 | *
card.number
Card number as it appears on the card.
50 | *
email
Email address of the customer required
51 | *
name
Customer name required
52 | *
reference
Reference field for external applications use.
53 | *
subscriptions.amount
Amount of payment in minor units. Example: 1000 = 10.00
54 | *
subscriptions.coupon
Coupon associated with the subscription for the customer.
55 | *
subscriptions.currency
Currency code (ISO-4217). Must match the currency associated with your 56 | * account. default:USD
57 | *
subscriptions.customer
The customer ID to create the subscription for. Do not supply this 58 | * when creating a customer.
59 | *
subscriptions.frequency
Frequency of payment for the plan. Example: Monthly
60 | *
subscriptions.name
Name describing subscription
61 | *
subscriptions.plan
The plan ID that the subscription should be created from.
62 | *
subscriptions.quantity
Quantity of the plan for the subscription.
63 | *
token
If specified, card associated with card token will be used
64 | * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and 65 | * Simplify::private_key are used. For backwards compatibility the public and 66 | * private keys may be passed instead of the authentication object. 67 | * @return Customer a Customer object. 68 | */ 69 | public static function createCustomer($hash, $authentication = null) 70 | { 71 | $args = func_get_args(); 72 | $authentication = SimplifyPaymentsApi::buildAuthenticationObject($authentication, $args, 2); 73 | 74 | $instance = new SimplifyCustomer(); 75 | $instance->setAll($hash); 76 | 77 | $object = SimplifyPaymentsApi::createObject($instance, $authentication); 78 | return $object; 79 | } 80 | 81 | /** 82 | * Deletes an SimplifyCustomer object. 83 | * 84 | * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key 85 | * and Simplify::private_key are used. For backwards compatibility the public and private keys may be passed 86 | * instead of the authentication object. 87 | */ 88 | public function deleteCustomer($authentication = null) 89 | { 90 | $args = func_get_args(); 91 | $authentication = SimplifyPaymentsApi::buildAuthenticationObject($authentication, $args, 1); 92 | 93 | SimplifyPaymentsApi::deleteObject($this, $authentication); 94 | $this->properties = null; 95 | return true; 96 | } 97 | 98 | 99 | /** 100 | * Retrieve SimplifyCustomer objects. 101 | * @param array criteria a map of parameters; valid keys are:
102 | *
filter
Filters to apply to the list.
103 | *
max
Allows up to a max of 50 list items to return. default:20
104 | *
offset
Used in paging of the list. This is the start offset of the page. default:0
105 | *
sorting
Allows for ascending or descending sorting of the list. The value maps properties to the sort direction 106 | * (either asc for ascending or desc for descending). Sortable properties are: dateCreated id name 107 | * email reference.
108 | * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and 109 | * Simplify::private_key are used. For backwards compatibility the public and private keys may be passed instead of the authentication object. 110 | * @return ResourceList a ResourceList object that holds the list of Customer objects and the total 111 | * number of Customer objects available for the given criteria. 112 | * @see ResourceList 113 | */ 114 | public static function listCustomer($criteria = null, $authentication = null) 115 | { 116 | $args = func_get_args(); 117 | $authentication = SimplifyPaymentsApi::buildAuthenticationObject($authentication, $args, 2); 118 | 119 | $val = new SimplifyCustomer(); 120 | $list = SimplifyPaymentsApi::listObject($val, $criteria, $authentication); 121 | 122 | return $list; 123 | } 124 | 125 | 126 | /** 127 | * Retrieve a SimplifyCustomer object from the API 128 | * 129 | * @param string id the id of the Customer object to retrieve 130 | * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and 131 | * Simplify::private_key are used. For backwards compatibility the public and 132 | * private keys may be passed instead of the authentication object. 133 | * @return Customer a Customer object 134 | */ 135 | public static function findCustomer($id, $authentication = null) 136 | { 137 | $args = func_get_args(); 138 | $authentication = SimplifyPaymentsApi::buildAuthenticationObject($authentication, $args, 2); 139 | 140 | $val = new SimplifyCustomer(); 141 | $val->id = $id; 142 | 143 | $obj = SimplifyPaymentsApi::findObject($val, $authentication); 144 | 145 | return $obj; 146 | } 147 | 148 | 149 | /** 150 | * Updates an SimplifyCustomer object. 151 | * 152 | * The properties that can be updated: 153 | *
    154 | *
  • card.addressCity (required)
  • 155 | *
  • card.addressCountry (required)
  • 156 | *
  • card.addressLine1 (required)
  • 157 | *
  • card.addressLine2 (required)
  • 158 | *
  • card.addressState (required)
  • 159 | *
  • card.addressZip (required)
  • 160 | *
  • card.cvc (required)
  • 161 | *
  • card.expMonth (required)
  • 162 | *
  • card.expYear (required)
  • 163 | *
  • card.id
  • 164 | *
  • card.name (required)
  • 165 | *
  • card.number
  • 166 | *
  • email (required)
  • 167 | *
  • name (required)
  • 168 | *
  • reference
  • 169 | *
  • token
  • 170 | *
171 | * @param $authentication - information used for the API call. If no value is passed the global keys Simplify::public_key and 172 | * Simplify::private_key are used. For backwards compatibility the public and 173 | * private keys may be passed instead of the authentication object. 174 | * @return Customer a Customer object. 175 | */ 176 | public function updateCustomer($authentication = null) 177 | { 178 | $args = func_get_args(); 179 | $authentication = SimplifyPaymentsApi::buildAuthenticationObject($authentication, $args, 1); 180 | 181 | $object = SimplifyPaymentsApi::updateObject($this, $authentication); 182 | return $object; 183 | } 184 | 185 | /** 186 | * @ignore 187 | */ 188 | public function getClazz() 189 | { 190 | return 'Customer'; 191 | } 192 | } -------------------------------------------------------------------------------- /src/css/style.css: -------------------------------------------------------------------------------- 1 | /************************************* 2 | * Simplify Module Settings Styles * 3 | **************************************/ 4 | 5 | #content { 6 | background: url('https://www.simplify.com/commerce/static/images/bg-gradient.png'); 7 | } 8 | 9 | .simplify-module-wrapper { 10 | clear: both; 11 | margin: 0px auto; 12 | min-width: 930px; 13 | max-width: 1024px; 14 | overflow: hidden; 15 | } 16 | 17 | .simplify-module-wrapper * { 18 | color: #333; 19 | font-size: 13px; 20 | line-height: 20px; 21 | } 22 | 23 | .simplify-module-wrapper .simplify-module-header { 24 | padding: 10px; 25 | clear: both; 26 | margin-bottom: 25px; 27 | padding-top: 15px; 28 | overflow: hidden; 29 | } 30 | 31 | .simplify-module-wrapper .simplify-settings h2{ 32 | font-size: 16px; 33 | font-weight: 600; 34 | } 35 | 36 | .simplify-module-wrapper .simplify-settings a{ 37 | color: #F60; 38 | } 39 | 40 | .simplify-module-wrapper .simplify-settings a:hover { 41 | text-decoration: underline; 42 | } 43 | /* close commented backslash hack */ 44 | 45 | .simplify-module-wrapper .marketing { 46 | width: 33%; 47 | text-align: center; 48 | padding: 0 15px; 49 | -webkit-box-sizing: border-box; 50 | -moz-box-sizing: border-box; 51 | box-sizing: border-box; 52 | } 53 | 54 | .simplify-module-wrapper .marketing h1 { 55 | font-weight: 600; 56 | font-size: 18px; 57 | margin: 10px 0; 58 | } 59 | 60 | .simplify-module-wrapper .header-title { 61 | width: 720px; 62 | text-align: center; 63 | } 64 | 65 | .simplify-module-wrapper .header-title h1 { 66 | font-size: 20px; 67 | } 68 | 69 | .simplify-module-wrapper .header-title h2 { 70 | font-size: 25px; 71 | font-weight: 600; 72 | margin: 0; 73 | } 74 | 75 | .simplify-module-wrapper .api-key-container { 76 | margin: 10px; 77 | } 78 | 79 | .simplify-module-wrapper .api-key-container h4 { 80 | color: #F26722; 81 | margin: 0; 82 | font-weight: normal; 83 | font-size: 16px; 84 | } 85 | 86 | .simplify-module-wrapper .api-keys { 87 | width: auto; 88 | background-color: #FAFAFA; 89 | padding: 5px; 90 | border: 1px solid #EEE; 91 | -webkit-border-radius: 5px; 92 | -moz-border-radius: 5px; 93 | border-radius: 5px; 94 | margin: 5px 0 20px 0; 95 | } 96 | 97 | .simplify-module-wrapper .api-keys .api-key-header div { 98 | font-weight: bold; 99 | font-size: 105%; 100 | } 101 | .simplify-module-wrapper .api-keys .api-key-box div, .simplify-module-wrapper .api-keys .api-key-header div { 102 | padding: 2px 5px; 103 | margin: 0 5px 5px 5px; 104 | } 105 | 106 | .simplify-module-wrapper .api-keys .api-key-key { 107 | width: 450px; 108 | } 109 | 110 | .simplify-module-wrapper .api-keys input { 111 | width: 390px; 112 | } 113 | 114 | .simplify-module-wrapper .btn, 115 | .simplify-module-wrapper .btn:visited { 116 | background-color: #F60!important; 117 | color: #FFF !important; 118 | -webkit-transition: background-color .25s ease-in; 119 | -moz-transition: background-color .25s ease-in; 120 | -o-transition: background-color .25s ease-in; 121 | transition: background-color .25s ease-in; 122 | -webkit-border-radius: 0; 123 | -moz-border-radius: 0; 124 | border-radius: 0; 125 | display: inline-block; 126 | font-size: 16px; 127 | zoom: 1; 128 | -webkit-transition: background-color .25s ease-in,color .25s ease-in; 129 | -moz-transition: background-color .25s ease-in,color .25s ease-in; 130 | -ms-transition: background-color .25s ease-in,color .25s ease-in; 131 | -o-transition: background-color .25s ease-in,color .25s ease-in; 132 | transition: background-color .25s ease-in,color .25s ease-in; 133 | padding: 10px 20px; 134 | text-decoration: none; 135 | text-shadow: 0 1px 2px rgba(0, 0, 0, 0.25); 136 | border-bottom: 1px solid rgba(0, 0, 0, 0.25); 137 | position: relative; 138 | cursor: pointer; 139 | border: 0; 140 | text-align: center; 141 | vertical-align: middle; 142 | } 143 | 144 | .simplify-module-wrapper .btn span, 145 | .simplify-module-wrapper .btn:visited span { 146 | color: white; 147 | } 148 | 149 | .simplify-module-wrapper .half { 150 | width: 48%; 151 | } 152 | 153 | .simplify-module-wrapper .radioInput{ 154 | margin: 10px; 155 | } 156 | 157 | .simplify-module-wrapper .container { 158 | padding: 0 15px; 159 | -webkit-box-sizing: border-box; 160 | -moz-box-sizing: border-box; 161 | box-sizing: border-box; 162 | } 163 | 164 | .simplify-module-wrapper .msg-container { 165 | background-color: #FFF; 166 | padding: 10px; 167 | border: 1px solid #CCC; 168 | font-size: 16px; 169 | margin-bottom: 10px; 170 | 171 | -webkit-border-radius: 5px; 172 | -moz-border-radius: 5px; 173 | border-radius: 5px; 174 | } 175 | 176 | .simplify-module-wrapper .settings-btn { 177 | margin-top: 25px; 178 | font-size: 14px; 179 | padding: 10px 24px; 180 | } 181 | 182 | .simplify-module-wrapper .technical-checks { 183 | width: 450px; 184 | float: right; 185 | background-color: #F0F0F0; 186 | padding: 0 10px 10px 10px; 187 | } 188 | 189 | .simplify-module-wrapper .formContainer { 190 | padding: 10px; 191 | border: 1px solid #CCC; 192 | background-color: white; 193 | -webkit-border-radius: 5px; 194 | -moz-border-radius: 5px; 195 | border-radius: 5px; 196 | -moz-box-shadow: 0 1px 3px 1px rgba(0,0,0,0.2); 197 | -webkit-box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.2); 198 | box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.2); 199 | margin: 5px 200 | } 201 | 202 | .simplify-module-wrapper .formContainer form { 203 | position: relative; 204 | top: -20px; 205 | } 206 | 207 | .simplify-module-wrapper .section { 208 | background-color: #FFF; 209 | border: 1px solid #CCC; 210 | padding: 15px 0; 211 | -webkit-border-radius: 5px; 212 | -moz-border-radius: 5px; 213 | border-radius: 5px; 214 | -moz-box-shadow: 0 1px 3px 1px rgba(0,0,0,0.2); 215 | -webkit-box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.2); 216 | box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.2); 217 | margin: 5px; 218 | margin-bottom: 25px; 219 | } 220 | 221 | .simplify-module-wrapper .keyModeContainer, .simplify-module-wrapper .saveCustomerDetailsContainer { 222 | text-align: center; 223 | background-color: #FAFAFA; 224 | padding: 5px; 225 | border: 1px solid #EEE; 226 | } 227 | 228 | .simplify-module-wrapper .keyModeContainer span { 229 | position: relative; 230 | top: 3px; 231 | } 232 | 233 | /************************* 234 | * Payment Form Styles * 235 | **************************/ 236 | 237 | #simplify-payment-form { 238 | color: #333; 239 | } 240 | 241 | #simplify-payment-form label { 242 | display: inline-block; 243 | margin-bottom: 4px; 244 | text-transform: uppercase; 245 | } 246 | 247 | #simplify-payment-form label.lowercase { 248 | text-transform: none; 249 | } 250 | 251 | #simplify-payment-form input[type="text"], 252 | #simplify-payment-form select { 253 | border: #CCCCCC solid 1px; 254 | margin-bottom: 15px; 255 | padding: 3px; 256 | } 257 | 258 | #simplify-payment-form input[type="checkbox"] { 259 | margin-bottom: 15px; 260 | } 261 | 262 | #simplify-payment-form input.simplify-card-number { 263 | padding: 7px 10px; 264 | width: 210px; 265 | } 266 | 267 | #simplify-payment-form a { 268 | display: inline; 269 | position: relative; 270 | } 271 | 272 | #simplify-payment-form a.simplify-card-cvc-info div.cvc-info { 273 | background-color: #BDE5F8; 274 | border: #00529B solid 1px; 275 | -moz-border-radius: 3px; 276 | -webkit-border-radius: 3px; 277 | border-radius: 3px; 278 | color: #00529B!important; 279 | display: none; 280 | font-style: normal; 281 | padding: 8px; 282 | position: absolute; 283 | right: -320px; top: -10px; 284 | text-decoration: none; 285 | width: 300px; 286 | } 287 | 288 | #simplify-payment-form a.simplify-card-cvc-info:hover div.cvc-info { 289 | display: block; 290 | } 291 | 292 | #simplify-payment-form div.cvc-info:hover { 293 | cursor: default; 294 | } 295 | 296 | #simplify-payment-form .block-left { 297 | display: inline-block; 298 | float: left; 299 | margin-right: 20px; 300 | } 301 | 302 | .simplifyFormContainer img.disable { 303 | opacity: 0.3; 304 | } 305 | 306 | i.simplifyFormContainer mg.enable { 307 | opacity: 1; 308 | } 309 | 310 | .simplify-submit-button-cc { 311 | margin-left: 5px; 312 | } 313 | 314 | .simplify-submit-button { 315 | float: right; 316 | position: relative; 317 | top: 45px; 318 | 319 | -webkit-transition: background-color .25s ease-in; 320 | -moz-transition: background-color .25s ease-in; 321 | -o-transition: background-color .25s ease-in; 322 | transition: background-color .25s ease-in; 323 | } 324 | 325 | .simplify-submit-button, 326 | .simplify-submit-button-cc { 327 | position:relative; 328 | display:inline-block; 329 | padding:5px 7px; 330 | border:1px solid #cc9900; 331 | -moz-border-radius: 3px; 332 | -webkit-border-radius: 3px; 333 | border-radius: 3px; 334 | font-weight:bold; 335 | color:#000; 336 | background:url(../../themes/default/img/bg_bt.gif) repeat-x 0 0 #f4b61b; 337 | cursor: pointer; 338 | } 339 | 340 | .simplify-submit-button:disabled, 341 | .simplify-submit-button-cc:disabled { 342 | text-decoration:none; 343 | background: #CCC; 344 | border-color: #AAA; 345 | } 346 | 347 | .simplifyFormContainer .simplify-payment-errors { 348 | margin: 0 0 10px 0; 349 | padding: 10px; 350 | border: 1px solid #900; 351 | font-size: 13px; 352 | background: #FCC; 353 | display: none; 354 | } 355 | 356 | #simplify-ajax-loader { 357 | display: none; 358 | text-align: center; 359 | background-color: #FFF; 360 | padding: 10px; 361 | font-size: 16px; 362 | border: 1px solid #CCC; 363 | margin-bottom: 20px; 364 | -webkit-border-radius: 5px; 365 | -moz-border-radius: 5px; 366 | border-radius: 5px; 367 | } 368 | 369 | #simplify-ajax-loader img { 370 | position: relative; 371 | top: 3px; 372 | margin-left: 10px; 373 | } 374 | 375 | #simplify-payment-form input, 376 | #simplify-payment-form select { 377 | margin-left: 0; 378 | } 379 | 380 | .simplifyFormContainer .secure-icon { 381 | position: relative; 382 | top: -5px; 383 | margin-right: 10px; 384 | } 385 | 386 | .simplifyFormContainer .test-msg { 387 | 388 | } 389 | 390 | .simplifyFormContainer .card-detail { 391 | width: 30%; 392 | } 393 | 394 | .simplifyFormContainer .card-detail.first { 395 | width: 50px; 396 | } 397 | 398 | .simplifyFormContainer .card-detail-label { 399 | font-size: 14px; 400 | margin-bottom: 10px; 401 | color: #555; 402 | } 403 | 404 | .simplifyFormContainer .card-detail-text { 405 | font-size: 12px; 406 | font-weight: 700; 407 | } 408 | 409 | .simplifyFormContainer .new-card-note { 410 | font-size: 11px; 411 | color: #555; 412 | } 413 | 414 | .simplifyFormContainer .card-type-container { 415 | border: 1px solid transparent; 416 | padding: 10px; 417 | 418 | -webkit-transition: all .25s ease-in; 419 | -moz-transition: all .25s ease-in; 420 | -o-transition: all .25s ease-in; 421 | transition: all .25s ease-in; 422 | } 423 | 424 | .simplifyFormContainer .card-type-container.selected { 425 | border: 1px solid #CCC; 426 | border-radius: 5px; 427 | background-color: #FFF; 428 | } 429 | 430 | .simplifyFormContainer .error-msg { 431 | color: #CF0101; 432 | float: right; 433 | font-size: 15px; 434 | margin-top: 18px; 435 | } 436 | 437 | .simplifyFormContainer.box { 438 | padding: 14px 18px 13px; 439 | margin: 0 0 30px 0; 440 | } 441 | 442 | #trash-icon { 443 | position: relative; 444 | top: -10px; 445 | cursor: pointer; 446 | } 447 | 448 | #cc-deletion-container { 449 | width: 115px; 450 | } 451 | 452 | #cc-confirm-deletion { 453 | display: none; 454 | color: #555; 455 | } 456 | 457 | #cc-confirm-deletion span { 458 | padding-right: 5px; 459 | cursor: pointer; 460 | } 461 | 462 | #cc-deletion-msg { 463 | text-align: center; 464 | font-size: 15px; 465 | color: #555; 466 | display: none; 467 | margin-bottom: 10px; 468 | } 469 | 470 | #cc-undo-deletion-lnk { 471 | cursor: pointer; 472 | } 473 | 474 | #simplify-test-mode-msg { 475 | color: #CF0101; 476 | display: none; 477 | font-size: 16px; 478 | margin-left: 10px; 479 | text-transform: capitalize; 480 | } 481 | 482 | .simplifyFormContainer .payment-cards { 483 | margin: 10px 0 25px 0; 484 | } 485 | 486 | .simplifyFormContainer .checker { 487 | float: left; 488 | position: relative; 489 | top: 5px; 490 | } 491 | 492 | /* Helper Styles */ 493 | 494 | .clearfix:after { 495 | visibility: hidden; 496 | display: block; 497 | font-size: 0; 498 | content: " "; 499 | clear: both; 500 | height: 0; 501 | } 502 | 503 | .clearfix { 504 | display: inline-block; 505 | } 506 | 507 | * html .simplify-clearfix { 508 | height: 1%; 509 | } 510 | 511 | .clearfix { 512 | display: block; 513 | } 514 | 515 | .left { 516 | float: left; 517 | } 518 | 519 | .right { 520 | float: right; 521 | } 522 | 523 | .bold { 524 | font-weight: 700; 525 | } 526 | 527 | .center { 528 | text-align: center; 529 | } 530 | 531 | .indent { 532 | margin-left: 50px; 533 | margin-top: 20px; 534 | } 535 | 536 | .small{ 537 | font-size: 13px; 538 | } 539 | 540 | .pad-botom { 541 | padding-bottom: 5px; 542 | } 543 | 544 | .underline { 545 | text-decoration: underline; 546 | } -------------------------------------------------------------------------------- /src/lib/Simplify/Http.php: -------------------------------------------------------------------------------- 1 | self::POST, 59 | 'put' => self::PUT, 60 | 'get' => self::GET, 61 | 'delete' => self::DELETE); 62 | 63 | private function request($url, $method, $authentication, $payload = '') 64 | { 65 | if ($authentication->public_key == null) 66 | throw new InvalidArgumentException('Must have a valid public key to connect to the API'); 67 | 68 | if ($authentication->private_key == null) 69 | throw new InvalidArgumentException('Must have a valid API key to connect to the API'); 70 | 71 | if (!array_key_exists(Tools::strtolower($method), self::$valid_methods)) 72 | throw new InvalidArgumentException('Invalid method: '.Tools::strtolower($method)); 73 | 74 | $method = self::$valid_methods[Tools::strtolower($method)]; 75 | 76 | $curl = curl_init(); 77 | 78 | $options = array(); 79 | 80 | $options[CURLOPT_URL] = $url; 81 | $options[CURLOPT_CUSTOMREQUEST] = $method; 82 | $options[CURLOPT_RETURNTRANSFER] = true; 83 | $options[CURLOPT_FAILONERROR] = false; 84 | 85 | $signature = $this->jwsEncode($authentication, $url, $payload, $method == self::POST || $method == self::PUT); 86 | 87 | if ($method == self::POST || $method == self::PUT) 88 | { 89 | $headers = array( 90 | 'Content-type: application/json' 91 | ); 92 | $options[CURLOPT_POSTFIELDS] = $signature; 93 | } 94 | else 95 | { 96 | $headers = array( 97 | 'Authorization: JWS '.$signature 98 | ); 99 | } 100 | 101 | array_push($headers, 'Accept: application/json'); 102 | $user_agent = 'PHP-SDK/'.SimplifyConstants::VERSION; 103 | if (Simplify::$user_agent != null) 104 | $user_agent = $user_agent.' '.Simplify::$user_agent; 105 | 106 | array_push($headers, 'User-Agent: '.$user_agent); 107 | 108 | $options[CURLOPT_HTTPHEADER] = $headers; 109 | 110 | curl_setopt_array($curl, $options); 111 | 112 | $data = curl_exec($curl); 113 | $errno = curl_errno($curl); 114 | $status = curl_getinfo($curl, CURLINFO_HTTP_CODE); 115 | 116 | if ($data == false || $errno != CURLE_OK) 117 | throw new SimplifyApiConnectionException(curl_error($curl)); 118 | 119 | $object = Tools::jsonDecode($data, true); 120 | $response = array('status' => $status, 'object' => $object); 121 | 122 | return $response; 123 | } 124 | 125 | /** 126 | * Handles Simplify API requests 127 | * 128 | * @param $url 129 | * @param $method 130 | * @param $authentication 131 | * @param string $payload 132 | * @return mixed 133 | * @throws SimplifyAuthenticationException 134 | * @throws SimplifyObjectNotFoundException 135 | * @throws SimplifyBadRequestException 136 | * @throws SimplifyNotAllowedException 137 | * @throws SimplifySystemException 138 | */ 139 | public function apiRequest($url, $method, $authentication, $payload = '') 140 | { 141 | $response = $this->request($url, $method, $authentication, $payload); 142 | 143 | $status = $response['status']; 144 | $object = $response['object']; 145 | 146 | if ($status == self::HTTP_SUCCESS) 147 | return $object; 148 | 149 | if ($status == self::HTTP_REDIRECTED) 150 | throw new SimplifyBadRequestException('Unexpected response code returned from the API, have you got the correct URL?', $status, $object); 151 | else if ($status == self::HTTP_BAD_REQUEST) 152 | throw new SimplifyBadRequestException('Bad request', $status, $object); 153 | else if ($status == self::HTTP_UNAUTHORIZED) 154 | throw new SimplifyAuthenticationException('You are not authorized to make this request. Are you using the correct API keys?', $status, $object); 155 | else if ($status == self::HTTP_NOT_FOUND) 156 | throw new SimplifyObjectNotFoundException('Object not found', $status, $object); 157 | else if ($status == self::HTTP_NOT_ALLOWED) 158 | throw new SimplifyNotAllowedException('Operation not allowed', $status, $object); 159 | else if ($status < 500) 160 | throw new SimplifyBadRequestException('Bad request', $status, $object); 161 | throw new SimplifySystemException('An unexpected error has been raised. Looks like there is something wrong at our end.', $status, $object); 162 | } 163 | 164 | /** 165 | * Handles Simplify OAuth requests 166 | * 167 | * @param $url 168 | * @param $payload 169 | * @param $authentication 170 | * @return mixed 171 | * @throws SimplifyAuthenticationException 172 | * @throws SimplifyObjectNotFoundException 173 | * @throws SimplifyBadRequestException 174 | * @throws SimplifyNotAllowedException 175 | * @throws SimplifySystemException 176 | */ 177 | public function oauthRequest($url, $payload, $authentication) 178 | { 179 | $response = $this->request($url, SimplifyHTTP::POST, $authentication, $payload); 180 | 181 | $status = $response['status']; 182 | $object = $response['object']; 183 | 184 | if ($status == self::HTTP_SUCCESS) 185 | return $object; 186 | 187 | $error = $object['error']; 188 | $error_description = $object['error_description']; 189 | 190 | if ($status == self::HTTP_REDIRECTED) 191 | throw new SimplifyBadRequestException('Unexpected response code returned from the API, have you got the correct URL?', $status, $object); 192 | else if ($status == self::HTTP_BAD_REQUEST) 193 | { 194 | if ($error == 'invalid_request') 195 | throw new SimplifyBadRequestException('', $status, 196 | $this->buildOauthError('Error during OAuth request', $error, $error_description)); 197 | else if ($error == 'unsupported_grant_type') 198 | throw new SimplifyBadRequestException('', $status, 199 | $this->buildOauthError('Unsupported grant type in OAuth request', $error, $error_description)); 200 | else if ($error == 'invalid_scope') 201 | throw new SimplifyBadRequestException('', $status, 202 | $this->buildOauthError('Invalid scope in OAuth request', $error, $error_description)); 203 | else 204 | throw new SimplifyBadRequestException('', $status, 205 | $this->buildOauthError('Unknown OAuth error', $error, $error_description)); 206 | } 207 | else if ($status == self::HTTP_UNAUTHORIZED) 208 | { 209 | if ($error == 'access_denied') 210 | throw new SimplifyAuthenticationException('', $status, 211 | $this->buildOauthError('Access denied for OAuth request', $error, $error_description)); 212 | else if ($error == 'invalid_client') 213 | throw new SimplifyAuthenticationException('', $status, 214 | $this->buildOauthError('Invalid client ID in OAuth request', $error, $error_description)); 215 | else if ($error == 'unauthorized_client') 216 | throw new SimplifyAuthenticationException('', $status, 217 | $this->buildOauthError('Unauthorized client in OAuth request', $error, $error_description)); 218 | else 219 | throw new SimplifyAuthenticationException('', $status, 220 | $this->buildOauthError('Unknown authentication error', $error, $error_description)); 221 | } 222 | else if ($status < 500) 223 | throw new SimplifyBadRequestException('Bad request', $status, $object); 224 | throw new SimplifySystemException('An unexpected error has been raised. Looks like there is something wrong at our end.', $status, $object); 225 | } 226 | 227 | public function jwsDecode($authentication, $hash) 228 | { 229 | if ($authentication->public_key == null) 230 | throw new InvalidArgumentException('Must have a valid public key to connect to the API'); 231 | 232 | if ($authentication->private_key == null) 233 | throw new InvalidArgumentException('Must have a valid API key to connect to the API'); 234 | 235 | if (!isset($hash['payload'])) 236 | throw new InvalidArgumentException('Event data is Missing payload'); 237 | 238 | $payload = trim($hash['payload']); 239 | 240 | try 241 | { 242 | $parts = explode('.', $payload); 243 | if (count($parts) != 3) 244 | $this->jwsAuthError('Incorrectly formatted JWS message'); 245 | 246 | $header_str = $this->jwsUrlSafeDecode64($parts[0]); 247 | $body_str = $this->jwsUrlSafeDecode64($parts[1]); 248 | $sig_str = $parts[2]; 249 | 250 | $url = null; 251 | if (isset($hash['url'])) 252 | $url = $hash['url']; 253 | 254 | $this->jwsVerifyHeader($header_str, $url, $authentication->public_key); 255 | 256 | $msg = $parts[0].'.'.$parts[1]; 257 | if (!$this->jwsVerifySignature($authentication->private_key, $msg, $sig_str)) 258 | $this->jwsAuthError('JWS signature does not match'); 259 | 260 | return $body_str; 261 | 262 | } 263 | catch (ApiException $e) 264 | { 265 | throw $e; 266 | } 267 | catch (Exception $e) 268 | { 269 | $this->jwsAuthError('Exception during JWS decoding: '.$e); 270 | } 271 | } 272 | 273 | private function jwsEncode($authentication, $url, $payload, $has_payload) 274 | { 275 | $jws_hdr = array('typ' => self::JWS_TYPE, 276 | 'alg' => self::JWS_ALGORITHM, 277 | 'kid' => $authentication->public_key, 278 | self::JWS_HDR_URI => $url, 279 | self::JWS_HDR_TIMESTAMP => sprintf('%u000', round(microtime(true))), 280 | self::JWS_HDR_NONCE => sprintf('%u', mt_rand()), 281 | ); 282 | 283 | // add oauth token if provided 284 | if (!empty($authentication->access_token)) 285 | $jws_hdr[self::JWS_HDR_TOKEN] = $authentication->access_token; 286 | 287 | $header = $this->jwsUrlSafeEncode64(Tools::jsonEncode($jws_hdr)); 288 | 289 | if ($has_payload) 290 | $payload = $this->jwsUrlSafeEncode64($payload); 291 | else 292 | $payload = ''; 293 | 294 | $msg = $header.'.'.$payload; 295 | return $msg.'.'.$this->jwsSign($authentication->private_key, $msg); 296 | } 297 | 298 | private function jwsSign($private_key, $msg) 299 | { 300 | $decoded_private_key = $this->jwsUrlSafeDecode64($private_key); 301 | $sig = hash_hmac('sha256', $msg, $decoded_private_key, true); 302 | 303 | return $this->jwsUrlSafeEncode64($sig); 304 | } 305 | 306 | private function jwsVerifyHeader($header, $url, $public_key) 307 | { 308 | $hdr = Tools::jsonDecode($header, true); 309 | 310 | if (count($hdr) != self::JWS_NUM_HEADERS) 311 | $this->jwsAuthError('Incorrect number of JWS header parameters - found '.count($hdr).' required '.self::JWS_NUM_HEADERS); 312 | 313 | if ($hdr['alg'] != self::JWS_ALGORITHM) 314 | $this->jwsAuthError('Incorrect algorithm - found '.$hdr['alg'].' required '.self::WS_ALGORITHM); 315 | 316 | if ($hdr['typ'] != self::JWS_TYPE) 317 | $this->jwsAuthError('Incorrect type - found '.$hdr['typ'].' required '.self::JWS_TYPE); 318 | 319 | if ($hdr['kid'] == null) 320 | $this->jwsAuthError('Missing Key ID'); 321 | 322 | if ($hdr['kid'] != $public_key) 323 | { 324 | if ($this->isLiveKey($public_key)) 325 | $this->jwsAuthError('Invalid Key ID'); 326 | } 327 | 328 | if ($hdr[self::JWS_HDR_URI] == null) 329 | $this->jwsAuthError('Missing URI'); 330 | 331 | if ($url != null && $hdr[self::JWS_HDR_URI] != $url) 332 | $this->jwsAuthError('Incorrect URL - found '.$hdr[self::JWS_HDR_URI].' required '.$url); 333 | 334 | if ($hdr[self::JWS_HDR_TIMESTAMP] == null) 335 | $this->jwsAuthError('Missing timestamp'); 336 | 337 | if (!$this->jwsVerifyTimestamp($hdr[self::JWS_HDR_TIMESTAMP])) 338 | $this->jwsAuthError('Invalid timestamp'); 339 | 340 | if ($hdr[self::JWS_HDR_NONCE] == null) 341 | $this->jwsAuthError('Missing nonce'); 342 | 343 | if ($hdr[self::JWS_HDR_UNAME] == null) 344 | $this->jwsAuthError('Missing username'); 345 | } 346 | 347 | private function jwsVerifySignature($private_key, $msg, $expected_sig) 348 | { 349 | return $this->jwsSign($private_key, $msg) == $expected_sig; 350 | } 351 | 352 | private function jwsAuthError($reason) 353 | { 354 | throw new SimplifyAuthenticationException('JWS authentication failure: '.$reason); 355 | } 356 | 357 | private function jwsVerifyTimestamp($ts) 358 | { 359 | $now = round(microtime(true)); // Seconds 360 | return abs($now - $ts / 1000) < self::JWS_MAX_TIMESTAMP_DIFF; 361 | } 362 | 363 | private function isLiveKey($k) 364 | { 365 | return strpos($k, 'lvpb') === 0; 366 | } 367 | 368 | private function jwsUrlSafeEncode64($s) 369 | { 370 | return str_replace(array('+', '/', '='), 371 | array('-', '_', ''), 372 | base64_encode($s)); 373 | } 374 | 375 | private function jwsUrlSafeDecode64($s) 376 | { 377 | switch (Tools::strlen($s) % 4) 378 | { 379 | case 0: 380 | break; 381 | case 2: $s = $s.'=='; 382 | break; 383 | case 3: $s = $s.'='; 384 | break; 385 | default: 386 | throw new InvalidArgumentException('incorrecly formatted JWS payload'); 387 | } 388 | return base64_decode(str_replace(array('-', '_'), array('+', '/'), $s)); 389 | } 390 | 391 | private function buildOauthError($msg, $error, $error_description) 392 | { 393 | return array( 394 | 'error' => array( 395 | 'code' => 'oauth_error', 396 | 'message' => $msg.', error code: '.$error.', description: '.$error_description.'' 397 | ) 398 | ); 399 | } 400 | } 401 | -------------------------------------------------------------------------------- /src/simplifycommerce.php: -------------------------------------------------------------------------------- 1 | name = 'simplifycommerce'; 52 | $this->tab = 'payments_gateways'; 53 | $this->version = '1.0.2'; 54 | $this->author = 'MasterCard'; 55 | $this->need_instance = 0; 56 | 57 | parent::__construct(); 58 | 59 | $this->displayName = $this->l('Simplify Commerce'); 60 | $this->description = $this->l('Payments made easy - Start securely accepting credit card payments instantly.'); 61 | $this->confirmUninstall = $this->l('Warning: Are you sure you want to uninstall this module?'); 62 | 63 | if (_PS_VERSION_ < '1.5') 64 | { 65 | $this->backward_error = $this->l('In order to work properly in PrestaShop v1.4, 66 | the Simplify Commerce module requires the backward compatibility module at least v0.3.').'
'. 67 | $this->l('You can download this module for free here: http://addons.prestashop.com/en/modules-prestashop/6222-backwardcompatibility.html'); 68 | 69 | if (file_exists(_PS_MODULE_DIR_.'backwardcompatibility/backward_compatibility/backward.php')) 70 | { 71 | include(_PS_MODULE_DIR_.'backwardcompatibility/backward_compatibility/backward.php'); 72 | $this->backward = true; 73 | } 74 | else 75 | $this->warning = $this->backward_error; 76 | } 77 | else 78 | $this->backward = true; 79 | } 80 | 81 | /** 82 | * Simplify Commerce's module installation 83 | * 84 | * @return boolean Install result 85 | */ 86 | public function install() 87 | { 88 | if (!$this->backward && _PS_VERSION_ < 1.5) 89 | { 90 | echo '
'.Tools::safeOutput($this->backward_error).'
'; 91 | return false; 92 | } 93 | 94 | /* For 1.4.3 and less compatibility */ 95 | $update_config = array( 96 | 'PS_OS_CHEQUE' => 1, 97 | 'PS_OS_PAYMENT' => 2, 98 | 'PS_OS_PREPARATION' => 3, 99 | 'PS_OS_SHIPPING' => 4, 100 | 'PS_OS_DELIVERED' => 5, 101 | 'PS_OS_CANCELED' => 6, 102 | 'PS_OS_REFUND' => 7, 103 | 'PS_OS_ERROR' => 8, 104 | 'PS_OS_OUTOFSTOCK' => 9, 105 | 'PS_OS_BANKWIRE' => 10, 106 | 'PS_OS_PAYPAL' => 11, 107 | 'PS_OS_WS_PAYMENT' => 12 108 | ); 109 | 110 | foreach ($update_config as $u => $v) 111 | { 112 | if (!Configuration::get($u) || (int)Configuration::get($u) < 1) 113 | { 114 | if (defined('_'.$u.'_') && (int)constant('_'.$u.'_') > 0) 115 | Configuration::updateValue($u, constant('_'.$u.'_')); 116 | else 117 | Configuration::updateValue($u, $v); 118 | } 119 | 120 | $ret = parent::install() && $this->registerHook('payment') && $this->registerHook('orderConfirmation') 121 | && Configuration::updateValue('SIMPLIFY_MODE', 0) && Configuration::updateValue('SIMPLIFY_SAVE_CUSTOMER_DETAILS', 1) 122 | && Configuration::updateValue('SIMPLIFY_PAYMENT_ORDER_STATUS', (int)Configuration::get('PS_OS_PAYMENT')) && $this->createDatabaseTables(); 123 | 124 | return $ret; 125 | } 126 | } 127 | 128 | /** 129 | * Simplify Customer tables creation 130 | * 131 | * @return boolean Database tables installation result 132 | */ 133 | public function createDatabaseTables() 134 | { 135 | return Db::getInstance()->Execute(' 136 | CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'simplify_customer` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT, 137 | `customer_id` varchar(32) NOT NULL, `simplify_customer_id` varchar(32) NOT NULL, `date_created` datetime NOT NULL, PRIMARY KEY (`id`), 138 | KEY `customer_id` (`customer_id`), KEY `simplify_customer_id` (`simplify_customer_id`)) ENGINE='. 139 | _MYSQL_ENGINE_.' DEFAULT CHARSET=utf8 AUTO_INCREMENT=1'); 140 | } 141 | 142 | /** 143 | * Simplify Commerce's module uninstallation. Remove the config values and delete the tables. 144 | * 145 | * @return boolean Uninstall result 146 | */ 147 | public function uninstall() 148 | { 149 | $uninstall_code = parent::uninstall(); 150 | $delete_mode_table = Configuration::deleteByName('SIMPLIFY_MODE'); 151 | $delete_customer_table = Configuration::deleteByName('SIMPLIFY_SAVE_CUSTOMER_DETAILS'); 152 | $delete_public_test_table = Configuration::deleteByName('SIMPLIFY_PUBLIC_KEY_TEST'); 153 | $delete_public_live_table = Configuration::deleteByName('SIMPLIFY_PUBLIC_KEY_LIVE'); 154 | $delete_private_test_table = Configuration::deleteByName('SIMPLIFY_PRIVATE_KEY_TEST'); 155 | $delete_private_live_table = Configuration::deleteByName('SIMPLIFY_PRIVATE_KEY_LIVE'); 156 | $delete_status_table = Configuration::deleteByName('SIMPLIFY_PAYMENT_ORDER_STATUS'); 157 | $delete_drop_table = Db::getInstance()->Execute('DROP TABLE `'._DB_PREFIX_.'simplify_customer`'); 158 | 159 | $ret1 = $uninstall_code && $delete_mode_table && $delete_customer_table && $delete_public_test_table && $delete_public_live_table; 160 | $ret2 = $delete_private_test_table && $delete_private_live_table && $delete_status_table && $delete_drop_table; 161 | 162 | return $ret1 && $ret2; 163 | } 164 | 165 | /** 166 | * Display the Simplify Commerce's payment form 167 | * 168 | * @return string Simplify Commerce's payment form 169 | */ 170 | public function hookPayment() 171 | { 172 | // If 1.4 and no backward then leave 173 | if (!$this->backward) 174 | return; 175 | 176 | // If the currency is not supported, then leave 177 | if (!in_array($this->context->currency->iso_code, $this->limited_currencies)) 178 | return; 179 | 180 | include(dirname(__FILE__).'/lib/Simplify.php'); 181 | 182 | $api_keys = $this->getSimplifyAPIKeys(); 183 | Simplify::$public_key = $api_keys->public_key; 184 | Simplify::$private_key = $api_keys->private_key; 185 | 186 | // If flag checked in the settings, look up customer details in the DB 187 | if (Configuration::get('SIMPLIFY_SAVE_CUSTOMER_DETAILS')) 188 | { 189 | $this->smarty->assign('show_save_customer_details_checkbox', true); 190 | $simplify_customer_id = Db::getInstance()->getValue('SELECT simplify_customer_id FROM '. 191 | _DB_PREFIX_.'simplify_customer WHERE customer_id = '.(int)$this->context->cookie->id_customer); 192 | 193 | if ($simplify_customer_id) 194 | { 195 | // look up the customer's details 196 | try 197 | { 198 | $customer = SimplifyCustomer::findCustomer($simplify_customer_id); 199 | $this->smarty->assign('show_saved_card_details', true); 200 | $this->smarty->assign('customer_details', $customer); 201 | } 202 | catch (SimplifyApiException $e) 203 | { 204 | if (class_exists('Logger')) 205 | Logger::addLog($this->l('Simplify Commerce - Error retrieving customer'), 1, null, 'Cart', (int)$this->context->cart->id, true); 206 | 207 | if ($e->getErrorCode() == 'object.not.found') 208 | $this->deleteCustomerFromDB(); // remove the old customer from the database, as it no longer exists in Simplify 209 | } 210 | } 211 | } 212 | 213 | // Create empty object by default 214 | $cardholder_details = new stdClass; 215 | 216 | // Send the cardholder's details with the payment 217 | if (isset($this->context->cart->id_address_invoice)) 218 | { 219 | $invoice_address = new Address((int)$this->context->cart->id_address_invoice); 220 | 221 | if ($invoice_address->id_state) 222 | { 223 | $state = new State((int)$invoice_address->id_state); 224 | 225 | if (Validate::isLoadedObject($state)) 226 | $invoice_address->state = $state->iso_code; 227 | } 228 | 229 | $cardholder_details = $invoice_address; 230 | } 231 | 232 | // Set js variables to send in card tokenization 233 | $this->smarty->assign('simplify_public_key', Simplify::$public_key); 234 | 235 | $this->smarty->assign('firstname', $cardholder_details->firstname); 236 | $this->smarty->assign('lastname', $cardholder_details->lastname); 237 | $this->smarty->assign('city', $cardholder_details->city); 238 | $this->smarty->assign('address1', $cardholder_details->address1); 239 | $this->smarty->assign('address2', $cardholder_details->address2); 240 | $this->smarty->assign('state', $cardholder_details->state); 241 | $this->smarty->assign('postcode', $cardholder_details->postcode); 242 | 243 | // Load JS and CSS files through CCC 244 | $this->context->controller->addCSS($this->_path.'css/style.css'); 245 | $this->context->controller->addJS('https://www.simplify.com/commerce/v1/simplify.js'); 246 | $this->context->controller->addJS($this->_path.'js/simplify.js'); 247 | $this->context->controller->addJS($this->_path.'js/simplify.form.js'); 248 | 249 | return $this->display(__FILE__, 'views/templates/hook/payment.tpl'); 250 | } 251 | 252 | /** 253 | * Display a confirmation message after an order has been placed. 254 | * 255 | * @param array $params Hook parameters 256 | * @return string Simplify Commerce's payment confirmation screen 257 | */ 258 | public function hookOrderConfirmation($params) 259 | { 260 | if (!isset($params['objOrder']) || ($params['objOrder']->module != $this->name)) 261 | return false; 262 | 263 | if ($params['objOrder'] && Validate::isLoadedObject($params['objOrder']) && isset($params['objOrder']->valid)) 264 | $this->smarty->assign('simplify_order', array('reference' => 265 | isset($params['objOrder']->reference) ? $params['objOrder']->reference : '#'. 266 | sprintf('%06d', $params['objOrder']->id), 'valid' => $params['objOrder']->valid)); 267 | 268 | return $this->display(__FILE__, 'views/templates/hook/order-confirmation.tpl'); 269 | } 270 | 271 | /** 272 | * Process a payment with Simplify Commerce. 273 | * Depeding on the customer's input, we can delete/update 274 | * existing customer card details and charge a payment 275 | * from the generated card token. 276 | */ 277 | public function processPayment() 278 | { 279 | // If 1.4 and no backward, then leave 280 | if (!$this->backward) 281 | return; 282 | 283 | // Extract POST paramaters from the request 284 | $simplify_token_post = Tools::getValue('simplifyToken'); 285 | $delete_customer_card_post = Tools::getValue('deleteCustomerCard'); 286 | $save_customer_post = Tools::getValue('saveCustomer'); 287 | $charge_customer_card = Tools::getValue('chargeCustomerCard'); 288 | 289 | $token = !empty($simplify_token_post) ? $simplify_token_post : null; 290 | $should_delete_customer = !empty($delete_customer_card_post) ? $delete_customer_card_post : false; 291 | $should_save_customer = !empty($save_customer_post) ? $save_customer_post : false; 292 | $should_charge_customer_card = !empty($charge_customer_card) ? $charge_customer_card : false; 293 | 294 | include(dirname(__FILE__).'/lib/Simplify.php'); 295 | $api_keys = $this->getSimplifyAPIKeys(); 296 | Simplify::$public_key = $api_keys->public_key; 297 | Simplify::$private_key = $api_keys->private_key; 298 | 299 | // look up the customer 300 | $simplify_customer = Db::getInstance()->getRow(' 301 | SELECT simplify_customer_id FROM '._DB_PREFIX_.'simplify_customer 302 | WHERE customer_id = '.(int)$this->context->cookie->id_customer); 303 | 304 | $simplify_customer_id = $this->getSimplifyCustomerID($simplify_customer['simplify_customer_id']); 305 | 306 | // The user has chosen to delete the credit card, so we need to delete the customer 307 | if (isset($simplify_customer_id) && $should_delete_customer) 308 | { 309 | try 310 | { 311 | // delete on simplify.com 312 | $customer = SimplifyCustomer::findCustomer($simplify_customer_id); 313 | $customer->deleteCustomer(); 314 | } 315 | catch (SimplifyApiException $e) 316 | { 317 | // can't find the customer on Simplify, so no need to delete 318 | if (class_exists('Logger')) 319 | Logger::addLog($this->l('Simplify Commerce - Error retrieving customer'), 1, null, 'Cart', (int)$this->context->cart->id, true); 320 | } 321 | 322 | $this->deleteCustomerFromDB(); 323 | $simplify_customer_id = null; 324 | } 325 | 326 | // The user has chosen to save the credit card details 327 | if ($should_save_customer == 'on') 328 | { 329 | // Customer exists already so update the card details from the card token 330 | if (isset($simplify_customer_id)) 331 | { 332 | try 333 | { 334 | $customer = SimplifyCustomer::findCustomer($simplify_customer_id); 335 | $updates = array( 336 | 'email' => $this->context->cookie->email, 337 | 'name' => $this->context->cookie->customer_firstname.' '.$this->context->cookie->customer_lastname, 338 | 'token' => $token 339 | ); 340 | 341 | $customer->setAll($updates); 342 | $customer->updateCustomer(); 343 | } 344 | catch (SimplifyApiException $e) 345 | { 346 | if (class_exists('Logger')) 347 | Logger::addLog($this->l('Simplify Commerce - Error updating customer card details'), 1, null, 'Cart', (int)$this->context->cart->id, true); 348 | } 349 | } 350 | else 351 | $simplify_customer_id = $this->createNewSimplifyCustomer($token); // Create a new customer from the card token 352 | } 353 | 354 | $charge = $this->context->cart->getOrderTotal(); 355 | 356 | try 357 | { 358 | $amount = $charge * 100; // Cart total amount 359 | $description = $this->context->shop->name.$this->l(' Order Number: ').(int)$this->context->cart->id; 360 | 361 | if (isset($simplify_customer_id) && ($should_charge_customer_card == 'true' || $should_save_customer == 'on')) 362 | { 363 | $simplify_payment = SimplifyPayment::createPayment(array( 364 | 'amount' => $amount, 365 | 'customer' => $simplify_customer_id, // Customer stored in the database 366 | 'description' => $description, 367 | 'currency' => 'USD' 368 | )); 369 | } 370 | else 371 | { 372 | $simplify_payment = SimplifyPayment::createPayment(array( 373 | 'amount' => $amount, 374 | 'token' => $token, // Token returned by Simplify Card Tokenization 375 | 'description' => $description, 376 | 'currency' => 'USD' 377 | )); 378 | } 379 | 380 | $payment_status = $simplify_payment->paymentStatus; 381 | } 382 | catch(SimplifyApiException $e) 383 | { 384 | $this->failPayment($e->getMessage()); 385 | } 386 | 387 | if ($payment_status != 'APPROVED') 388 | $this->failPayment('The transaction was '.$payment_status); 389 | 390 | // Log the transaction 391 | $order_status = (int)Configuration::get('SIMPLIFY_PAYMENT_ORDER_STATUS'); 392 | $message = $this->l('Simplify Commerce Transaction Details:').'\n\n'. 393 | $this->l('Payment ID:').' '.$simplify_payment->id.'\n'. 394 | $this->l('Payment Status:').' '.$simplify_payment->paymentStatus.'\n'. 395 | $this->l('Amount:').' '.$simplify_payment->amount * 0.01.'\n'. 396 | $this->l('Currency:').' '.$simplify_payment->currency.'\n'. 397 | $this->l('Description:').' '.$simplify_payment->description.'\n'. 398 | $this->l('Auth Code:').' '.$simplify_payment->authCode.'\n'. 399 | $this->l('Fee:').' '.$simplify_payment->fee * 0.01.'\n'. 400 | $this->l('Card Last 4:').' '.$simplify_payment->card->last4.'\n'. 401 | $this->l('Card Expiry Year:').' '.$simplify_payment->card->expYear.'\n'. 402 | $this->l('Card Expiry Month:').' '.$simplify_payment->card->expMonth.'\n'. 403 | $this->l('Card Type:').' '.$simplify_payment->card->type.'\n'; 404 | 405 | // Create the PrestaShop order in database 406 | $this->validateOrder((int)$this->context->cart->id, (int)$order_status, $charge, 407 | $this->displayName, $message, array(), null, false, $this->context->customer->secure_key); 408 | 409 | if (version_compare(_PS_VERSION_, '1.5', '>=')) 410 | { 411 | $new_order = new Order((int)$this->currentOrder); 412 | 413 | if (Validate::isLoadedObject($new_order)) 414 | { 415 | $payment = $new_order->getOrderPaymentCollection(); 416 | 417 | if (isset($payment[0])) 418 | { 419 | $payment[0]->transaction_id = pSQL($simplify_payment->id); 420 | $payment[0]->save(); 421 | } 422 | } 423 | } 424 | 425 | if (_PS_VERSION_ < 1.5) 426 | $redirect = __PS_BASE_URI__.'order-confirmation.php?id_cart='.(int)$this->context->cart->id.'&id_module='. 427 | (int)$this->id.'&id_order='.(int)$this->currentOrder.'&key='.$this->context->customer->secure_key; 428 | else 429 | $redirect = __PS_BASE_URI__.'index.php?controller=order-confirmation&id_cart='.(int)$this->context->cart->id.'&id_module='. 430 | (int)$this->id.'&id_order='.(int)$this->currentOrder.'&key='.$this->context->customer->secure_key.'&payment_status='.$payment_status; 431 | 432 | header('Location: '.$redirect); 433 | exit; 434 | } 435 | 436 | /** 437 | * Function to check if customer still exists in Simplify and if not to delete them from the DB. 438 | * 439 | * @return string Simplify customer's id. 440 | */ 441 | private function getSimplifyCustomerID($customer_id) 442 | { 443 | $simplify_customer_id = null; 444 | 445 | try 446 | { 447 | $customer = SimplifyCustomer::findCustomer($customer_id); 448 | $simplify_customer_id = $customer->id; 449 | } 450 | catch (SimplifyApiException $e) 451 | { 452 | // can't find the customer on Simplify, so no need to delete 453 | if (class_exists('Logger')) 454 | Logger::addLog($this->l('Simplify Commerce - Error retrieving customer'), 1, null, 'Cart', (int)$this->context->cart->id, true); 455 | 456 | if ($e->getErrorCode() == 'object.not.found') 457 | $this->deleteCustomerFromDB(); // remove the old customer from the database, as it no longer exists in Simplify 458 | } 459 | 460 | return $simplify_customer_id; 461 | } 462 | 463 | /** 464 | * Function to create a new Simplify customer and to store its id in the database. 465 | * 466 | * @return string Simplify customer's id. 467 | */ 468 | private function deleteCustomerFromDB() 469 | { 470 | Db::getInstance()->Execute('DELETE FROM '._DB_PREFIX_.'simplify_customer WHERE customer_id = '.(int)$this->context->cookie->id_customer.';'); 471 | } 472 | 473 | /** 474 | * Function to create a new Simplify customer and to store its id in the database. 475 | * 476 | * @return string Simplify customer's id. 477 | */ 478 | private function createNewSimplifyCustomer($token) 479 | { 480 | try 481 | { 482 | $customer = SimplifyCustomer::createCustomer(array( 483 | 'email' => $this->context->cookie->email, 484 | 'name' => $this->context->cookie->customer_firstname.' '.$this->context->cookie->customer_lastname, 485 | 'token' => $token, 486 | 'reference' => $this->context->shop->name.$this->l(' Customer ID:').' '.(int)$this->context->cookie->id_customer 487 | )); 488 | 489 | $simplify_customer_id = $customer->id; 490 | 491 | Db::getInstance()->Execute(' 492 | INSERT INTO '._DB_PREFIX_.'simplify_customer (id, customer_id, simplify_customer_id, date_created) 493 | VALUES (NULL, '.(int)$this->context->cookie->id_customer.', \''.$simplify_customer_id.'\', NOW())'); 494 | } 495 | catch(SimplifyApiException $e) 496 | { 497 | $this->failPayment($e->getMessage()); 498 | } 499 | 500 | return $simplify_customer_id; 501 | } 502 | 503 | /** 504 | * Function to return the user's Simplify API Keys depending on the account mode in the settings. 505 | * 506 | * @return object Simple object containin the Simplify public & private key values. 507 | */ 508 | private function getSimplifyAPIKeys() 509 | { 510 | $api_keys = new stdClass; 511 | $api_keys->public_key = Configuration::get('SIMPLIFY_MODE') ? 512 | Configuration::get('SIMPLIFY_PUBLIC_KEY_LIVE') : Configuration::get('SIMPLIFY_PUBLIC_KEY_TEST'); 513 | $api_keys->private_key = Configuration::get('SIMPLIFY_MODE') ? 514 | Configuration::get('SIMPLIFY_PRIVATE_KEY_LIVE') : Configuration::get('SIMPLIFY_PRIVATE_KEY_TEST'); 515 | 516 | return $api_keys; 517 | } 518 | 519 | /** 520 | * Function to log a failure message and redirect the user 521 | * back to the payment processing screen with the error. 522 | * 523 | * @param string $message Error message to log and to display to the user 524 | */ 525 | private function failPayment($message) 526 | { 527 | if (class_exists('Logger')) 528 | Logger::addLog($this->l('Simplify Commerce - Payment transaction failed').' '.$message, 1, null, 'Cart', (int)$this->context->cart->id, true); 529 | 530 | $controller = Configuration::get('PS_ORDER_PROCESS_TYPE') ? 'order-opc.php' : 'order.php'; 531 | error_log($message); 532 | $location = $this->context->link->getPageLink($controller).(strpos($controller, '?') !== false ? '&' : '?'). 533 | 'step=3&simplify_error=There was a problem with your payment: '.$message.'#simplify_error'; 534 | header('Location: '.$location); 535 | exit; 536 | } 537 | 538 | /** 539 | * Check settings requirements to make sure the Simplify Commerce's 540 | * API keys are set. 541 | * 542 | * @return boolean Whether the API Keys are set or not. 543 | */ 544 | public function checkSettings() 545 | { 546 | if (Configuration::get('SIMPLIFY_MODE')) 547 | return Configuration::get('SIMPLIFY_PUBLIC_KEY_LIVE') != '' && Configuration::get('SIMPLIFY_PRIVATE_KEY_LIVE') != ''; 548 | else 549 | return Configuration::get('SIMPLIFY_PUBLIC_KEY_TEST') != '' && Configuration::get('SIMPLIFY_PRIVATE_KEY_TEST') != ''; 550 | } 551 | 552 | /** 553 | * Check technical requirements to make sure the Simplify Commerce's module will work properly 554 | * 555 | * @return array Requirements tests results 556 | */ 557 | public function checkRequirements() 558 | { 559 | $tests = array('result' => true); 560 | $tests['curl'] = array('name' => $this->l('PHP cURL extension must be enabled on your server'), 'result' => extension_loaded('curl')); 561 | 562 | if (Configuration::get('SIMPLIFY_MODE')) 563 | $tests['ssl'] = array('name' => $this->l('SSL must be enabled on your store (before entering Live mode)'), 'result' => 564 | Configuration::get('PS_SSL_ENABLED') || (!empty($_SERVER['HTTPS']) && Tools::strtolower($_SERVER['HTTPS']) != 'off')); 565 | 566 | $tests['currencies'] = array('name' => $this->l('The currency USD must be enabled on your store'), 'result' => 567 | Currency::exists('GBP', 0) || Currency::exists('EUR', 0) || Currency::exists('USD', 0) || Currency::exists('CAD', 0)); 568 | $tests['php52'] = array('name' => $this->l('Your server must run PHP 5.3 or greater'), 'result' => version_compare(PHP_VERSION, '5.3.0', '>=')); 569 | $tests['configuration'] = array('name' => $this->l('You must set your Simplify Commerce API Keys'), 'result' => $this->checkSettings()); 570 | 571 | if (_PS_VERSION_ < 1.5) 572 | { 573 | $tests['backward'] = array('name' => $this->l('You are using the backward compatibility module'), 'result' => 574 | $this->backward, 'resolution' => $this->backward_error); 575 | $tmp = Module::getInstanceByName('mobile_theme'); 576 | 577 | if ($tmp && isset($tmp->version) && !version_compare($tmp->version, '0.3.8', '>=')) 578 | $tests['mobile_version'] = array('name' => $this->l('You are currently using the default mobile template, 579 | the minimum version required is v0.3.8'). 580 | ' (v'.$tmp->version.' '.$this->l('detected'). 581 | ' - '. 582 | $this->l('Please Upgrade').')', 'result' => version_compare($tmp->version, '0.3.8', '>=')); 583 | } 584 | 585 | foreach ($tests as $k => $test) 586 | if ($k != 'result' && !$test['result']) 587 | $tests['result'] = false; 588 | 589 | return $tests; 590 | } 591 | 592 | /** 593 | * Display the Simplify Commerce's module settings page 594 | * for the user to set their API Key pairs and choose 595 | * whether their customer's can save their card details for 596 | * repeate visits. 597 | * 598 | * @return string Simplify settings page 599 | */ 600 | public function getContent() 601 | { 602 | $output = ''; 603 | 604 | // Update Simplify settings 605 | if (Tools::isSubmit('SubmitSimplify')) 606 | { 607 | $configuration_values = array( 608 | 'SIMPLIFY_MODE' => Tools::getValue('simplify_mode'), 609 | 'SIMPLIFY_SAVE_CUSTOMER_DETAILS' => Tools::getValue('simplify_save_csutomer_details'), 610 | 'SIMPLIFY_PUBLIC_KEY_TEST' => Tools::getValue('simplify_public_key_test'), 611 | 'SIMPLIFY_PUBLIC_KEY_LIVE' => Tools::getValue('simplify_public_key_live'), 612 | 'SIMPLIFY_PRIVATE_KEY_TEST' => Tools::getValue('simplify_private_key_test'), 613 | 'SIMPLIFY_PRIVATE_KEY_LIVE' => Tools::getValue('simplify_private_key_live'), 614 | 'SIMPLIFY_PAYMENT_ORDER_STATUS' => (int)Tools::getValue('simplify_payment_status') 615 | ); 616 | 617 | foreach ($configuration_values as $configuration_key => $configuration_value) 618 | Configuration::updateValue($configuration_key, $configuration_value); 619 | } 620 | 621 | $requirements = $this->checkRequirements(); 622 | 623 | $output .= ' 624 | 625 | 626 |
627 | '.(Tools::isSubmit('SubmitSimplify') ? '
'.$this->l('Settings successfully saved'). 628 | '
' : '').' 630 |
631 | 632 | 633 |
634 |

Start accepting payments now.

635 |

It’s that simple.

636 |
637 | Sign up for free 638 |
639 |
640 |
641 |
642 |
643 | feature_signup.jpg 644 |

Easy sign up

645 |

Click the "Sign up for free" button and become a Simplify merchant for free.

646 |
647 |
648 |
649 |
650 | feature_signup.jpg 651 |

Simple pricing

652 |

No setup fees.
No monthly fees.
No minimum.

653 |
654 |
655 |
656 |
657 | feature_signup.jpg 658 |

Two-day funding

659 |

Deposits are made into your account in two business days for most transactions.

660 |
661 |
662 |
663 |
664 |
665 |
666 |

Technical Checks

667 |
'. 668 | $this->l('Good news! Everything looks to be in order. Start accepting credit card payments now.') : 669 | 'warn">'.$this->l('Unfortunately, at least one issue is preventing you from using Simplify Commerce. 670 | Please fix the issue and reload this page.')).'
671 | '; 672 | foreach ($requirements as $k => $requirement) 673 | if ($k != 'result') 674 | $output .= ' 675 | 676 | 677 | 679 | '; 680 | $output .= ' 681 |
'.$requirement['name'].(!$requirement['result'] && isset($requirement['resolution']) ? '
'. 678 | Tools::safeOutput($requirement['resolution'], true) : '').'
682 |
683 |
'; 684 | 685 | /* If 1.4 and no backward, then leave */ 686 | if (!$this->backward) 687 | return $output; 688 | 689 | $statuses = OrderState::getOrderStates((int)$this->context->cookie->id_lang); 690 | $output .= ' 691 |
692 |
693 |

API Key Mode

694 |
695 |
696 | Test Mode 698 | Live Mode 700 |
701 |

Test Mode
All transactions in test mode are test payments. You can test your installation using card numbers from our 702 | list of test card numbers. 703 | You cannot process real payments in test mode, so all other card numbers will be declined.

704 |

Live Mode
All transactions made in live mode are real payments and will be processed accordingly.

705 |
706 |

Set Your API Keys

707 | 711 |
712 |
713 |

Test

714 |
715 |
716 |
717 |
Private Key
718 |
Public Key
719 |
720 |
721 |
723 |
725 |
726 |
727 |
728 | 729 |
730 |
731 |

Live

732 |
733 |
734 |
735 |
Private Key
736 |
Public Key
737 |
738 |
739 |
741 |
743 |
744 |
745 |
746 |
747 |
748 |

Save Customer Details

749 | 758 |
759 |
'; 760 | 761 | $statuses_options = array(array('name' => 'simplify_payment_status', 'label' => 762 | $this->l('Sucessful Payment Order Status'), 'current_value' => Configuration::get('SIMPLIFY_PAYMENT_ORDER_STATUS'))); 763 | 764 | foreach ($statuses_options as $status_options) 765 | { 766 | $output .= ' 767 |

'.$status_options['label'].'

768 |

Choose the status for an order once the payment has been successfully processed by Simplify.

769 |
770 | 776 |
'; 777 | } 778 | 779 | $output .= ' 780 |
781 |
782 |
783 |
784 |
785 | 786 | 787 |
788 | '; 801 | 802 | return $output; 803 | } 804 | } 805 | ?> 806 | --------------------------------------------------------------------------------