├── .editorconfig ├── .gitattributes ├── .scrutinizer.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── _config.php ├── code-of-conduct.md ├── code ├── ChequePayment │ └── ChequePayment.php ├── DPSPayment │ ├── DPSAdapter.php │ ├── DPSPayment.php │ ├── DPSRecurringPayment.php │ └── thirdparty │ │ └── dps_hosted_helper │ │ ├── LICENSE │ │ ├── MifMessage.php │ │ ├── PxAccess.php │ │ ├── PxPay.php │ │ ├── PxPayMessage.php │ │ ├── PxPayRequest.php │ │ └── PxPayResponse.php ├── Eway │ ├── EwayConfig.inc.php │ ├── EwayPaymentLive.php │ └── EwayXMLPayment.php ├── PayPalPayment │ ├── PayPalAdapter.php │ └── PayPalPayment.php ├── PayStation │ ├── PaystationHostedPayment.php │ └── PaystationPayment.php ├── PayerHavingReceipt.php ├── Payment.php ├── RecurringPayment.php └── Worldpay │ └── WorldpayPayment.php ├── composer.json ├── images ├── payments │ ├── dps.gif │ ├── eway.gif │ ├── methods │ │ ├── american-express.gif │ │ ├── dinners-club.jpg │ │ ├── discover.jpg │ │ ├── jcb.jpg │ │ ├── mastercard.jpg │ │ ├── paypal.jpg │ │ └── visa.jpg │ ├── paypal.jpg │ ├── paystation.jpg │ └── worldpay.gif └── ui-bg.jpg ├── javascript └── Eway.js ├── lang ├── _manifest_exclude └── en_US.php └── templates └── Includes ├── DPSPayment.ss ├── DPSPayment_receipt.ss └── Payer_receipt.ss /.editorconfig: -------------------------------------------------------------------------------- 1 | # For more information about the properties used in this file, 2 | # please see the EditorConfig documentation: 3 | # http://editorconfig.org 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 4 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [{*.yml,package.json}] 14 | indent_size = 2 15 | 16 | # The indent size used in the package.json file cannot be changed: 17 | # https://github.com/npm/npm/pull/3180#issuecomment-16336516 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /tests export-ignore 2 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | inherit: true 2 | 3 | checks: 4 | php: 5 | verify_property_names: true 6 | verify_argument_usable_as_reference: true 7 | verify_access_scope_valid: true 8 | useless_calls: true 9 | use_statement_alias_conflict: true 10 | variable_existence: true 11 | unused_variables: true 12 | unused_properties: true 13 | unused_parameters: true 14 | unused_methods: true 15 | unreachable_code: true 16 | too_many_arguments: true 17 | sql_injection_vulnerabilities: true 18 | simplify_boolean_return: true 19 | side_effects_or_types: true 20 | security_vulnerabilities: true 21 | return_doc_comments: true 22 | return_doc_comment_if_not_inferrable: true 23 | require_scope_for_properties: true 24 | require_scope_for_methods: true 25 | require_php_tag_first: true 26 | psr2_switch_declaration: true 27 | psr2_class_declaration: true 28 | property_assignments: true 29 | prefer_while_loop_over_for_loop: true 30 | precedence_mistakes: true 31 | precedence_in_conditions: true 32 | phpunit_assertions: true 33 | php5_style_constructor: true 34 | parse_doc_comments: true 35 | parameter_non_unique: true 36 | parameter_doc_comments: true 37 | param_doc_comment_if_not_inferrable: true 38 | optional_parameters_at_the_end: true 39 | one_class_per_file: true 40 | no_unnecessary_if: true 41 | no_trailing_whitespace: true 42 | no_property_on_interface: true 43 | no_non_implemented_abstract_methods: true 44 | no_error_suppression: true 45 | no_duplicate_arguments: true 46 | no_commented_out_code: true 47 | newline_at_end_of_file: true 48 | missing_arguments: true 49 | method_calls_on_non_object: true 50 | instanceof_class_exists: true 51 | foreach_traversable: true 52 | fix_line_ending: true 53 | fix_doc_comments: true 54 | duplication: true 55 | deprecated_code_usage: true 56 | deadlock_detection_in_loops: true 57 | code_rating: true 58 | closure_use_not_conflicting: true 59 | catch_class_exists: true 60 | blank_line_after_namespace_declaration: false 61 | avoid_multiple_statements_on_same_line: true 62 | avoid_duplicate_types: true 63 | avoid_conflicting_incrementers: true 64 | avoid_closing_tag: true 65 | assignment_of_null_return: true 66 | argument_type_checks: true 67 | 68 | filter: 69 | paths: [code/*, tests/*] 70 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.4.1 - 2013-02-28 4 | 5 | * Security: Payment Information Leak in Test Harness Controller (see 0.3.2) 6 | * Security: XML Injection in DPSAdapter API Requests (see 0.3.2) 7 | 8 | ## 0.3.2 - 2013-02-28 9 | 10 | * Security: DPS Payment Information Leak in Test Harness Controller 11 | 12 | Since 2010, the payment module included a "test harness" controller 13 | ([commit](https://github.com/silverstripe-labs/silverstripe-payment/commit/8f27918294ac34b688f137e36b424616df55dd7f), 14 | which was not correctly secured against public access. 15 | It allowed a broad range of operations against the configured DPS API, 16 | including listing payments incl. amounts and transaction details, 17 | refunding and authenticate existing payments, create new payments. 18 | It does not expose the actual payment API credentials, customer or credit card details. 19 | The vulnerability also doesn't allow directing payments to a different account. 20 | 21 | This affects all recent versions of the module, but is limited to the 22 | DPS/PaymentExpress payment provider. 23 | 24 | We have removed the functionality from the module. If you are using 25 | the functionality, please port it into your own codebase and ensure 26 | the controller is secured to ADMIN permissions. 27 | As a hotfix, you can also remove code/Harness.php to secure the installation. 28 | In this case, don't forget to flush the manifest cache by appending ?flush=1 to any SilverStripe URL. 29 | 30 | Reporter: Nicolaas Thiemen-Francken 31 | 32 | * Security: XML Injection in DPSAdapter API Requests 33 | 34 | The `doPayment()`, `postConnect()` and `doDPSHostedPayment()` methods on `DPSAdapter` did not sanitize 35 | method arguments before constructing an XML request from it, and passing it on to the DPS API. 36 | Since these arguments are typically derived from user input, the method is considered unsafe. 37 | 38 | ## 0.4.0 - 2013-02-20 39 | 40 | * Security: Information Leak in DPSAdapter (see 0.3.1) 41 | 42 | ## 0.3.1 - 2013-02-20 43 | 44 | * Security: Information Leak in DPSAdapter 45 | 46 | Severity: Important 47 | 48 | Description: Exposure of DPS credentials through web URLs, routed through the `DPSAdapter` controller. 49 | 50 | Impact: An attacker might be able to simulate payments using live payment gateway credentials. 51 | With knowledge of the DPS transaction number, he could also operate on existing payments. 52 | In case credentials are reused for other logins, these might get compromised as well. 53 | The DPS [PXPost](http://www.paymentexpress.com/Technical_Resources/Ecommerce_NonHosted/PxPost) 54 | and [PXPay](http://www.paymentexpress.com/Technical_Resources/Ecommerce_Hosted/PxPay) 55 | APIs don't expose customer data and truncates credit card data, so the impact is limited. 56 | 57 | ## 0.3.0 58 | 59 | ## 0.2 60 | 61 | ## 0.1 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | * Copyright (c) 2008, Silverstripe Ltd. 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * * Neither the name of the nor the 12 | * names of its contributors may be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY Silverstripe Ltd. ``AS IS'' AND ANY 16 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL Silverstripe Ltd. BE LIABLE FOR ANY 19 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Payment Module 2 | 3 | *Important:* The module is being restructured for SilverStripe 3 support, see "Roadmap" below 4 | 5 | ## Overview 6 | 7 | Generic API for various payment gateways. 8 | Records payments and their status in the database. 9 | 10 | ## Supported Gateways and Payment Methods 11 | 12 | * [Payment Express](http://paymentexpress.com) (Merchant hosted) - Supports Auth, Complete, Purchase, Refund, Validate 13 | * [Payment Express](http://paymentexpress.com) (DPS hosted) - Supports Auth, Purchase 14 | * [Eway](http://www.eway.com.au/) 15 | * [PayPal](http://www.paypal.com) 16 | * [PayStation](http://www.paystation.com) 17 | * [Worldpay](http://www.worldpay.com) 18 | * Cheque Payment (manual processing) 19 | 20 | ## Configuration 21 | 22 | A project using this module needs to set its PDS account in project _config.php file, 23 | - If using DPS-hosted payment gateway (pxpost), set PXPost account: 24 | DPSAdapter::set_pxpost_account($your_pxpost_username, $your_pxpost_password); 25 | - If using Merchant-hosted payment geteway (pxpay), set PXPay account: 26 | DPSAdapter::set_pxpay_account($your_pxpay_userid, $your_pxpay_key); 27 | - If using both gateways, you need to set both above. This is very likely when using 28 | DPS-hosted to Auth a Credit Card and using Merchant-hosted to recursively pay. 29 | 30 | This module is a stand-alone module, it is only dependent on SilverStripe core. 31 | We have re-factored DPSPayment and make DPSPayment and previous DPSHostedPayment into one 32 | payment object, the only difference between the two is they call different functions when 33 | making a transaction. 34 | 35 | One of its common applications is to be used in E-commerce module. But in general, 36 | it should be hook-up with any data object as long as this data object is payable, 37 | such as a downloadable mp3, a E-book, booking a ticket on-line, donation, etc. 38 | DPSPayment has been re-implemented in this way, though we need to check all other payment 39 | methods in future releases. 40 | 41 | ## Roadmap 42 | 43 | The module was heavily worked on during GSOC 2012 44 | ([project page](https://github.com/silverstripe/gsoc-wiki/wiki/GSOC-2012-Project:-Improve-Payment-Module)), 45 | which resulted in a number of forks, which we'll merge back once they're in a good state. 46 | 47 | * https://github.com/frankmullenger/silverstripe-payment 48 | * https://github.com/frankmullenger/silverstripe-payment-paypal 49 | * https://github.com/frankmullenger/silverstripe-payment-paymentexpress 50 | * https://github.com/frankmullenger/silverstripe-payment-cheque 51 | * https://github.com/frankmullenger/silverstripe-payment-paystation 52 | * https://github.com/frankmullenger/silverstripe-payment-securepaytech 53 | * https://github.com/frankmullenger/silverstripe-gsoc-payment-test 54 | 55 | ## Troubleshooting 56 | 57 | ### Curl and CA Certificates on Windows 58 | 59 | Some gateways (like `DPSPayment`) use PHP's curl in order to submit data, 60 | usually through a secure SSL connection. In some cases, the CA certificates 61 | aren't accepted. On Windows, PHP's curl doesn't come with any root CAs installed. 62 | You'll need to add them manually. The easiest way is a global installation 63 | through changing `php.ini`: 64 | 65 | * Download [http://curl.haxx.se/ca/cacert.pem](http://curl.haxx.se/ca/cacert.pem) 66 | * Move into a common directory, e.g. `c:\php` 67 | * Configure the setting in your `php.ini`: `curl.cainfo=c:\php\cacert.pem` 68 | -------------------------------------------------------------------------------- /_config.php: -------------------------------------------------------------------------------- 1 | 'WorldpayPayment_Handler', 5 | PayPalPayment_Handler::$URLSegment . '/$Action/$ID' => 'PayPalPayment_Handler', 6 | )); 7 | 8 | Object::add_extension('Member', 'PayerHavingReceipt'); -------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | When having discussions about this module in issues or pull request please adhere to the [SilverStripe Community Code of Conduct](https://docs.silverstripe.org/en/contributing/code_of_conduct). 2 | -------------------------------------------------------------------------------- /code/ChequePayment/ChequePayment.php: -------------------------------------------------------------------------------- 1 | Status = 'Pending'; 17 | $this->Message = '

' . _t('ChequePayment.MESSAGE', 'Payment accepted via Cheque. Please note : products will not be shipped until payment has been received.') . '

'; 18 | 19 | $this->write(); 20 | return new Payment_Success(); 21 | } 22 | 23 | public function getPaymentFormFields() 24 | { 25 | return new FieldSet( 26 | // retrieve cheque content from the ChequeContent() method on this class 27 | new LiteralField("Chequeblurb", '
' . $this->ChequeContent() . '
'), 28 | new HiddenField("Cheque", "Cheque", 0) 29 | ); 30 | } 31 | 32 | public function getPaymentFormRequirements() 33 | { 34 | return null; 35 | } 36 | 37 | /** 38 | * Returns the Cheque content from the CheckoutPage 39 | */ 40 | public function ChequeContent() 41 | { 42 | if (class_exists('CheckoutPage')) { 43 | return DataObject::get_one('CheckoutPage')->ChequeMessage; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /code/DPSPayment/DPSAdapter.php: -------------------------------------------------------------------------------- 1 | 'Canadian Dollar', 41 | 'CHF'=>'Swiss Franc', 42 | 'EUR'=>'Euro', 43 | 'FRF'=>'French Franc', 44 | 'GBP'=>'United Kingdom Pound', 45 | 'HKD'=>'Hong Kong Dollar', 46 | 'JPY'=>'Japanese Yen', 47 | 'NZD'=>'New Zealand Dollar', 48 | 'SGD'=>'Singapore Dollar', 49 | 'USD'=>'United States Dollar', 50 | 'ZAR'=>'Rand', 51 | 'AUD'=>'Australian Dollar', 52 | 'WST'=>'Samoan Tala', 53 | 'VUV'=>'Vanuatu Vatu', 54 | 'TOP'=>"Tongan Pa'anga", 55 | 'SBD'=>'Solomon Islands Dollar', 56 | 'PGK'=>'Papua New Guinea Kina', 57 | 'MYR'=>'Malaysian Ringgit', 58 | 'KWD'=>'Kuwaiti Dinar', 59 | 'FJD'=>'Fiji Dollar' 60 | ); 61 | 62 | protected static $cvn_mode = true; 63 | 64 | protected static $credit_cards = array( 65 | 'Visa' => 'payment/images/payments/methods/visa.jpg', 66 | 'MasterCard' => 'payment/images/payments/methods/mastercard.jpg', 67 | 'American Express' => 'payment/images/payments/methods/american-express.gif', 68 | 'Dinners Club' => 'payment/images/payments/methods/dinners-club.jpg', 69 | 'JCB' => 'payment/images/payments/methods/jcb.jpg' 70 | ); 71 | 72 | public static function set_pxpost_account($pxPost_Username, $pxPost_Password) 73 | { 74 | self::$pxPost_Username = $pxPost_Username; 75 | self::$pxPost_Password = $pxPost_Password; 76 | } 77 | 78 | public static function set_pxpay_account($pxPay_Userid, $pxPay_Key) 79 | { 80 | self::$pxPay_Userid = $pxPay_Userid; 81 | self::$pxPay_Key = $pxPay_Key; 82 | } 83 | 84 | public static function get_pxpost_username() 85 | { 86 | return self::$pxPost_Username; 87 | } 88 | 89 | public static function get_pxpost_password() 90 | { 91 | return self::$pxPost_Password; 92 | } 93 | 94 | public static function get_pxpay_userid() 95 | { 96 | return self::$pxPay_Userid; 97 | } 98 | 99 | public static function get_pxpay_key() 100 | { 101 | return self::$pxPay_Key; 102 | } 103 | 104 | public static function remove_credit_card($creditCard) 105 | { 106 | unset(self::$credit_cards[$creditCard]); 107 | } 108 | 109 | public static function set_receipt_from($address) 110 | { 111 | self::$receipt_from = $address; 112 | } 113 | 114 | public static function get_receipt_from() 115 | { 116 | return self::$receipt_from; 117 | } 118 | 119 | public static function unset_cvn_mode() 120 | { 121 | self::$cvn_mode = false; 122 | } 123 | 124 | public static function set_mode($mode) 125 | { 126 | self::$mode = $mode; 127 | } 128 | 129 | public function postConnect() 130 | { 131 | $inputs['PostUsername'] = self::$pxPost_Username; 132 | $inputs['PostPassword'] = self::$pxPost_Password; 133 | $transaction = ""; 134 | foreach ($inputs as $name => $value) { 135 | $XML_name = Convert::raw2xml($name); 136 | $XML_value = Convert::raw2xml($value); 137 | $transaction .= "<$XML_name>$XML_value"; 138 | } 139 | $transaction .= ""; 140 | 141 | $client = $this->getHTTPClient(); 142 | $resultXml = $client->post(self::$pxPost_Url, $transaction); 143 | $match = preg_match('/^\(.*)\<\/Txn\>$/i', $resultXml, $matches); 144 | return $match; 145 | } 146 | 147 | //Payment Function 148 | public function doPayment($inputs, $payment) 149 | { 150 | // Main Settings 151 | //$inputs = array(); 152 | $inputs['PostUsername'] = self::$pxPost_Username; 153 | $inputs['PostPassword'] = self::$pxPost_Password; 154 | //$inputs = array(); 155 | 156 | // Transaction Creation 157 | $transaction = ""; 158 | foreach ($inputs as $name => $value) { 159 | if ($name == "Amount") { 160 | $value = number_format($value, 2, '.', ''); 161 | } 162 | $XML_name = Convert::raw2xml($name); 163 | $XML_value = Convert::raw2xml($value); 164 | $transaction .= "<$XML_name>$XML_value"; 165 | } 166 | $transaction .= ""; 167 | 168 | $client = $this->getHTTPClient(); 169 | $resultXml = $client->post(self::$pxPost_Url, $transaction); 170 | $payment->ResponseXML = $resultXml; 171 | 172 | // XML Parser Creation 173 | $xmlParser = xml_parser_create(); 174 | $values = null; 175 | $indexes = null; 176 | xml_parse_into_struct($xmlParser, $resultXml, $values, $indexes); 177 | xml_parser_free($xmlParser); 178 | 179 | // XML Result Parsed In A PHP Array 180 | $resultPhp = array(); 181 | $level = array(); 182 | foreach ($values as $xmlElement) { 183 | if ($xmlElement['type'] == 'open') { 184 | if (array_key_exists('attributes', $xmlElement)) { 185 | list($level[$xmlElement['level']], $extra) = array_values($xmlElement['attributes']); 186 | } else { 187 | $level[$xmlElement['level']] = $xmlElement['tag']; 188 | } 189 | } elseif ($xmlElement['type'] == 'complete') { 190 | $startLevel = 1; 191 | $phpArray = '$resultPhp'; 192 | while ($startLevel < $xmlElement['level']) { 193 | $phpArray .= '[$level['. $startLevel++ .']]'; 194 | } 195 | $phpArray .= '[$xmlElement[\'tag\']] = array_key_exists(\'value\', $xmlElement)? $xmlElement[\'value\'] : null;'; 196 | eval($phpArray); 197 | } 198 | } 199 | 200 | $responseFields = $resultPhp['TXN']; 201 | 202 | // DPS Response Management 203 | if ($responseFields['SUCCESS']) { 204 | $payment->Status = 'Success'; 205 | if ($authcode = $responseFields['1']['AUTHCODE']) { 206 | $payment->AuthCode = $authcode; 207 | } 208 | if ($dpsBillingID = $responseFields['1']['DPSBILLINGID']) { 209 | $payment->DPSBillingID = $dpsBillingID; 210 | } 211 | 212 | $dateSettlement = $responseFields['1']['DATESETTLEMENT']; 213 | $payment->SettlementDate = substr($dateSettlement, 0, 4) ."-".substr($dateSettlement, 4, 2)."-".substr($dateSettlement, 6, 2); 214 | $payment->Amount->Currency = $responseFields['1']['INPUTCURRENCYNAME']; 215 | $payment->DateExpiry = $responseFields['1']['DATEEXPIRY']; 216 | $payment->CardNumberTruncated = $responseFields['1']['CARDNUMBER']; 217 | $payment->CardHolderName = $responseFields['1']['CARDHOLDERNAME']; 218 | } else { 219 | $payment->Status = 'Failure'; 220 | } 221 | if ($transactionRef = @$responseFields['DPSTXNREF']) { 222 | $payment->TxnRef = $transactionRef; 223 | } 224 | if ($helpText = @$responseFields['HELPTEXT']) { 225 | $payment->Message = $helpText; 226 | } elseif ($responseText = @$responseFields['RESPONSETEXT']) { 227 | $payment->Message = $responseText; 228 | } 229 | 230 | $payment->write(); 231 | 232 | if (self::$mode === 'Rolling_Back_Mode' && self::$using_transaction) { 233 | DB::getConn()->transactionRollback(); 234 | } 235 | } 236 | 237 | public function getPxpay() 238 | { 239 | return new PxPay(self::$pxPay_Url, self::$pxPay_Userid, self::$pxPay_Key); 240 | } 241 | 242 | public function doDPSHostedPayment($inputs, $payment) 243 | { 244 | $request = new PxPayRequest(); 245 | foreach ($inputs as $element => $value) { 246 | $funcName = 'set' . $element; 247 | if (!is_object($value)) { 248 | $value = Convert::raw2xml($value); 249 | } 250 | $request->$funcName($value); 251 | } 252 | 253 | // submit payment request to get the URL for redirection 254 | $pxpay = $this->getPxpay(); 255 | $requestXML = $pxpay->makeRequest($request); 256 | $xml = new SimpleXMLElement($requestXML); 257 | $requestElement = $xml->xpath('//Request'); 258 | $valid = (bool) $requestElement[0]['valid']; 259 | if ($valid) { 260 | $urls = $xml->xpath('//URI'); 261 | $url = (string) $urls[0]; 262 | if (self::$using_transaction) { 263 | DB::getConn()->transactionEnd(); 264 | } 265 | if (self::$mode == 'Unit_Test_Only') { 266 | return $url; 267 | } else { 268 | if (Director::is_ajax()) { 269 | echo $url; 270 | die(); 271 | } else { 272 | header('Location: ' . $url); 273 | die(); 274 | } 275 | } 276 | } else { 277 | $payment->Message = 'Invalid Request String'; 278 | $payment->write(); 279 | } 280 | } 281 | 282 | /** 283 | * Action called by DPS right after a payment operation, being a success or a failure 284 | * Will update the status of the payment accordingly, and redirect to the success/failure url 285 | * where more domain specific operations can be performed 286 | * @see {http://www.paymentexpress.com/technical_resources/ecommerce_hosted/pxaccess.html#ResultNotification} 287 | */ 288 | public function processDPSHostedResponse() 289 | { 290 | $pxpay = new PxPay(self::$pxPay_Url, self::$pxPay_Userid, self::$pxPay_Key); 291 | 292 | $enc_hex = $_REQUEST["result"]; 293 | 294 | $rsp = $pxpay->getResponse($enc_hex); 295 | 296 | $paymentID = $rsp->getTxnData1(); 297 | $SQL_paymentID = (int)$paymentID; 298 | 299 | if ($dpsBillingID = $rsp->getDpsBillingId()) { 300 | $payment = DataObject::get_by_id('DPSRecurringPayment', $SQL_paymentID); 301 | $payment->DPSBillingID = $dpsBillingID; 302 | } else { 303 | $payment = DataObject::get_by_id("DPSPayment", $SQL_paymentID); 304 | } 305 | 306 | if ($payment) { 307 | if (self::$using_transaction) { 308 | DB::getConn()->transactionStart(); 309 | } 310 | try { 311 | $payment->ResponseXML = $rsp->toXml(); 312 | $success = $rsp->getSuccess(); 313 | if ($success =='1') { 314 | // @todo Use AmountSettlement for amount setting? 315 | $payment->Status="Success"; 316 | } else { 317 | $payment->Status="Failure"; 318 | } 319 | $payment->TxnRef = $rsp->getDpsTxnRef(); 320 | $payment->AuthCode = $rsp->getAuthCode(); 321 | $payment->DateExpiry = $rsp->getDateExpiry(); 322 | $payment->CardNumberTruncated = $rsp->getCardNumber(); 323 | $payment->CardHolderName = $rsp->getCardHolderName(); 324 | 325 | $payment->Message=$rsp->getResponseText(); 326 | $payment->write(); 327 | if (self::$using_transaction) { 328 | DB::getConn()->transactionEnd(); 329 | } 330 | } catch (Exception $e) { 331 | if (self::$using_transaction) { 332 | DB::getConn()->transactionRollback(); 333 | } 334 | $payment->handleError($e); 335 | } 336 | Director::redirect($payment->DPSHostedRedirectURL); 337 | } else { 338 | Director::redirect(Director::baseURL()); 339 | } 340 | } 341 | 342 | protected $httpClient; 343 | 344 | public function setHTTPClient($client) 345 | { 346 | $this->httpClient = $client; 347 | } 348 | 349 | public function getHTTPClient() 350 | { 351 | if (!$this->httpClient) { 352 | $this->httpClient = new DPSAdapter_HTTPClient(); 353 | } 354 | return $this->httpClient; 355 | } 356 | } 357 | 358 | /** 359 | * Crude HTTP client to allow for basic mocking of HTTP responses. 360 | */ 361 | class DPSAdapter_HTTPClient 362 | { 363 | 364 | public function post($url, $data) 365 | { 366 | $ch = curl_init(); 367 | curl_setopt($ch, CURLOPT_URL, $url); 368 | curl_setopt($ch, CURLOPT_POST, 1); 369 | curl_setopt($ch, CURLOPT_POSTFIELDS, $data); 370 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 371 | //curl_setopt($clientURL, CURLOPT_SSL_VERIFYPEER, 0); //Needs to be included if no *.crt is available to verify SSL certificates 372 | if (defined('CAINFO')) { 373 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1); 374 | curl_setopt($ch, CURLOPT_CAINFO, CAINFO); 375 | } else { 376 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); 377 | } 378 | 379 | $result = curl_exec($ch); 380 | 381 | if (curl_error($ch)) { 382 | throw new Exception(curl_error($ch)); 383 | } 384 | 385 | curl_close($ch); 386 | 387 | return $result; 388 | } 389 | } 390 | -------------------------------------------------------------------------------- /code/DPSPayment/DPSPayment.php: -------------------------------------------------------------------------------- 1 | 'Varchar(1024)', 12 | 'TxnType' => "Enum('Purchase,Auth,Complete,Refund,Validate', 'Purchase')", 13 | 'AuthCode' => 'Varchar(22)', 14 | 'MerchantReference' => 'Varchar(64)', 15 | 'DPSHostedRedirectURL' => 'Text', 16 | 17 | // This field is stored for only when the payment is made thru merchant-hosted DPS gateway (PxPost); 18 | 'SettlementDate' => 'Date', 19 | 20 | // We store the whole raw response xml in case that tracking back the payment is needed in a later stage for whatever the reason. 21 | 'ResponseXML' => "Text", 22 | 'CardNumberTruncated' => 'Varchar(32)', // The first six and two last digits of the CC 23 | 'CardHolderName' => 'Varchar(255)', 24 | 'DateExpiry' => 'Varchar(4)', // four digits (mm/yy) 25 | 'TimeOutDate' => 'SS_Datetime' 26 | ); 27 | 28 | public static $indexes = array( 29 | 'TxnRef' => true, 30 | ); 31 | 32 | public static $has_one = array( 33 | //in case that TxnType is Complete, the DPSPayment could have one Auth DPSPayment 34 | 'AuthPayment' => 'DPSPayment', 35 | 36 | //in case that TxnType is Refund, the DPSPayment could have one Refunded DPSPayment 37 | 'RefundedFor' => 'DPSPayment' 38 | ); 39 | 40 | private static $input_elements = array( 41 | 'Amount', 42 | 'CardHolderName', 43 | 'CardNumber', 44 | 'BillingId', 45 | 'Cvc2', 46 | 'DateExpiry', 47 | 'DpsBillingId', 48 | 'DpsTxnRef', 49 | 'EnableAddBillCard', 50 | 'InputCurrency', 51 | 'MerchantReference', 52 | 'Opt', 53 | 'PostUsername', 54 | 'PostPassword', 55 | 'TxnType', 56 | 'TxnData1', 57 | 'TxnData2', 58 | 'TxnData3', 59 | 'TxnId', 60 | 'EnableAvsData', 61 | 'AvsAction', 62 | 'AvsPostCode', 63 | 'AvsStreetAddress', 64 | 'DateStart', 65 | 'IssueNumber', 66 | 'Track2', 67 | ); 68 | 69 | private static $dpshosted_input_elements = array( 70 | 'PxPayUserId', 71 | 'PxPayKey', 72 | 'AmountInput', 73 | 'CurrencyInput', 74 | 'EmailAddress', 75 | 'EnableAddBillCard', 76 | 'MerchantReference', 77 | 'TxnData1', 78 | 'TxnData2', 79 | 'TxnData3', 80 | 'TxnType', 81 | 'TxnId', 82 | 'UrlFail', 83 | 'UrlSuccess', 84 | ); 85 | 86 | public static $default_sort = "ID DESC"; 87 | 88 | /** 89 | * Cached {@link SimpleXMLElement} object, containing ResponseXML. Indexed by Payment record ID. 90 | * @var SimpleXMLElement|null 91 | */ 92 | protected $cacheResponseXML = null; 93 | 94 | public function getPaymentFormFields() 95 | { 96 | $adapter = $this->getDPSAdapter(); 97 | return $adapter->getPaymentFormFields(); 98 | } 99 | 100 | /** 101 | * Returns the required fields to add to the order form, when using this payment method. 102 | */ 103 | public function getPaymentFormRequirements() 104 | { 105 | $adapter = $this->getDPSAdapter(); 106 | return $adapter->getPaymentFormRequirements(); 107 | } 108 | 109 | //This function is hooked with OrderForm/E-commerce at the moment, so we need to keep it as it is. 110 | public function processPayment($data, $form) 111 | { 112 | $inputs['Amount'] = $this->Amount->Amount; 113 | $inputs['InputCurrency'] = $this->Amount->Currency; 114 | $inputs['TxnData1'] = $this->ID; 115 | $inputs['TxnType'] = 'Purchase'; 116 | 117 | $inputs['CardHolderName'] = $data['CardHolderName']; 118 | $inputs['CardNumber'] = implode('', $data['CardNumber']); 119 | $inputs['DateExpiry'] = $data['DateExpiry']; 120 | if (self::$cvn_mode) { 121 | $inputs['Cvc2'] = $data['Cvc2'] ? $data['Cvc2'] : ''; 122 | } 123 | 124 | $adapter = $this->getDPSAdapter(); 125 | $responseFields = $adapter->doPayment($inputs); 126 | $adapter->ProcessResponse($this, $responseFields); 127 | 128 | if ($this->Status == 'Success') { 129 | $result = new Payment_Success(); 130 | } else { 131 | $result = new Payment_Failure(); 132 | } 133 | 134 | return $result; 135 | } 136 | 137 | public function auth($data) 138 | { 139 | if (DPSAdapter::$using_transaction) { 140 | DB::getConn()->transactionStart(); 141 | } 142 | try { 143 | $this->TxnType = "Auth"; 144 | $this->write(); 145 | 146 | $adapter = $this->getDPSAdapter(); 147 | $inputs = $this->prepareAuthInputs($data); 148 | $adapter->doPayment($inputs, $this); 149 | if (DPSAdapter::$using_transaction) { 150 | DB::getConn()->transactionEnd(); 151 | } 152 | } catch (Exception $e) { 153 | if (DPSAdapter::$using_transaction) { 154 | DB::getConn()->transactionRollback(); 155 | } 156 | $this->handleError($e); 157 | } 158 | } 159 | 160 | private function prepareAuthInputs($data) 161 | { 162 | //never put this loop after $inputs['AmountInput'] = $this->Amount->Amount;, since it will change it to an array. 163 | foreach ($data as $element => $value) { 164 | if (in_array($element, self::$input_elements)) { 165 | $inputs[$element] = $value; 166 | } 167 | } 168 | $inputs['TxnData1'] = $this->ID; 169 | $inputs['TxnType'] = $this->TxnType; 170 | $inputs['Amount'] = $this->Amount->Amount; 171 | $inputs['InputCurrency'] = $this->Amount->Currency; 172 | //special element 173 | $inputs['CardNumber'] = implode('', $data['CardNumber']); 174 | 175 | return $inputs; 176 | } 177 | 178 | public function complete() 179 | { 180 | if (DPSAdapter::$using_transaction) { 181 | DB::getConn()->transactionStart(); 182 | } 183 | try { 184 | $auth = $this->AuthPayment(); 185 | $this->TxnType = "Complete"; 186 | $this->MerchantReference = "Complete: ".$auth->MerchantReference; 187 | $this->write(); 188 | 189 | $adapter = $this->getDPSAdapter(); 190 | $inputs = $this->prepareCompleteInputs(); 191 | $adapter->doPayment($inputs, $this); 192 | if (DPSAdapter::$using_transaction) { 193 | DB::getConn()->transactionEnd(); 194 | } 195 | } catch (Exception $e) { 196 | if (DPSAdapter::$using_transaction) { 197 | DB::getConn()->transactionRollback(); 198 | } 199 | $this->handleError($e); 200 | } 201 | } 202 | 203 | private function prepareCompleteInputs() 204 | { 205 | $auth = $this->AuthPayment(); 206 | $inputs['TxnData1'] = $this->ID; 207 | $inputs['TxnType'] = $this->TxnType; 208 | $inputs['Amount'] = $this->Amount->Amount; 209 | $inputs['InputCurrency'] = $this->Amount->Currency; 210 | $inputs['DpsTxnRef'] = $auth->TxnRef; 211 | //$inputs['AuthCode'] = $auth->AuthCode; 212 | return $inputs; 213 | } 214 | 215 | public function purchase($data) 216 | { 217 | if (DPSAdapter::$using_transaction) { 218 | DB::getConn()->transactionStart(); 219 | } 220 | try { 221 | $this->TxnType = "Purchase"; 222 | $this->write(); 223 | 224 | $adapter = $this->getDPSAdapter(); 225 | $inputs = $this->prepareAuthInputs($data); 226 | $adapter->doPayment($inputs, $this); 227 | if (DPSAdapter::$using_transaction) { 228 | DB::getConn()->transactionEnd(); 229 | } 230 | } catch (Exception $e) { 231 | if (DPSAdapter::$using_transaction) { 232 | DB::getConn()->transactionRollback(); 233 | } 234 | $this->handleError($e); 235 | } 236 | } 237 | 238 | public function refund() 239 | { 240 | if (DPSAdapter::$using_transaction) { 241 | DB::getConn()->transactionStart(); 242 | } 243 | try { 244 | $refunded = $this->RefundedFor(); 245 | $this->TxnType = "Refund"; 246 | $this->MerchantReference = "Refund for: ".$refunded->MerchantReference; 247 | $this->write(); 248 | 249 | $adapter = $this->getDPSAdapter(); 250 | $inputs = $this->prepareRefundInputs(); 251 | $adapter->doPayment($inputs, $this); 252 | if (DPSAdapter::$using_transaction) { 253 | DB::getConn()->transactionEnd(); 254 | } 255 | } catch (Exception $e) { 256 | if (DPSAdapter::$using_transaction) { 257 | DB::getConn()->transactionRollback(); 258 | } 259 | $this->handleError($e); 260 | return false; 261 | } 262 | 263 | return true; 264 | } 265 | 266 | private function prepareRefundInputs() 267 | { 268 | $refundedFor = $this->RefundedFor(); 269 | $inputs['TxnData1'] = $this->ID; 270 | $inputs['TxnType'] = $this->TxnType; 271 | $inputs['Amount'] = $this->Amount->Amount; 272 | $inputs['InputCurrency'] = $this->Amount->Currency; 273 | $inputs['DpsTxnRef'] = $refundedFor->TxnRef; 274 | $inputs['MerchantReference'] = $this->MerchantReference; 275 | return $inputs; 276 | } 277 | 278 | public function dpshostedPurchase($data = array()) 279 | { 280 | if (DPSAdapter::$using_transaction) { 281 | DB::getConn()->transactionStart(); 282 | } 283 | try { 284 | $this->TxnType = "Purchase"; 285 | $this->write(); 286 | $adapter = $this->getDPSAdapter(); 287 | $inputs = $this->prepareDPSHostedRequest($data); 288 | 289 | return $adapter->doDPSHostedPayment($inputs, $this); 290 | } catch (Exception $e) { 291 | if (DPSAdapter::$using_transaction) { 292 | DB::getConn()->transactionRollback(); 293 | } 294 | $this->handleError($e); 295 | } 296 | } 297 | 298 | public function prepareDPSHostedRequest($data = array()) 299 | { 300 | //never put this loop after $inputs['AmountInput'] = $amount, since it will change it to an array. 301 | foreach ($data as $element => $value) { 302 | if (in_array($element, self::$dpshosted_input_elements)) { 303 | $inputs[$element] = $value; 304 | } 305 | } 306 | 307 | $inputs['TxnData1'] = $this->ID; 308 | $inputs['TxnType'] = $this->TxnType; 309 | $amount = (float) ltrim($this->Amount->Amount, '$'); 310 | $inputs['AmountInput'] = $amount; 311 | $inputs['InputCurrency'] = $this->Amount->Currency; 312 | $inputs['MerchantReference'] = $this->MerchantReference; 313 | if (isset($this->TimeOutDate)) { 314 | $inputs['Opt'] = $this->TimeOutDate; 315 | } 316 | 317 | $postProcess_url = Director::absoluteBaseURL() ."DPSAdapter/processDPSHostedResponse"; 318 | $inputs['UrlFail'] = $postProcess_url; 319 | $inputs['UrlSuccess'] = $postProcess_url; 320 | 321 | return $inputs; 322 | } 323 | 324 | public function payAsRecurring() 325 | { 326 | $adapter = $this->getDPSAdapter(); 327 | $inputs = $this->prepareAsRecurringPaymentInputs(); 328 | $adapter->doPayment($inputs, $this); 329 | } 330 | 331 | public function prepareAsRecurringPaymentInputs() 332 | { 333 | $reccurringPayment = DataObject::get_by_id('DPSRecurringPayment', $this->RecurringPaymentID); 334 | $inputs['DpsBillingId'] = $reccurringPayment->DPSBillingID; 335 | $inputs['TxnData1'] = $this->ID; 336 | $inputs['TxnType'] = 'Purchase'; 337 | $amount = (float) ltrim($reccurringPayment->Amount->Amount, '$'); 338 | $inputs['Amount'] = $amount; 339 | $inputs['InputCurrency'] = $reccurringPayment->Amount->Currency; 340 | $inputs['MerchantReference'] = $reccurringPayment->MerchantReference; 341 | 342 | return $inputs; 343 | } 344 | 345 | public function CanComplete() 346 | { 347 | $successComplete = $this->successCompletePayment(); 348 | return !($successComplete && $successComplete->ID) && $this->TxnType == 'Auth' && $this->Status = 'Success'; 349 | } 350 | 351 | public function successCompletePayment() 352 | { 353 | return DataObject::get_one( 354 | "DPSPayment", 355 | "\"Status\" = 'Success' AND \"TxnType\" = 'Complete' AND \"AuthPaymentID\" = '".(int)$this->ID."'" 356 | ); 357 | } 358 | 359 | 360 | public function onAfterWrite() 361 | { 362 | if ($this->isChanged('Status') && $this->Status == 'Success') { 363 | $this->sendReceipt(); 364 | } 365 | parent::onAfterWrite(); 366 | } 367 | 368 | public function sendReceipt() 369 | { 370 | $member = $this->PaidBy(); 371 | if ($member->exists() && $member->Email) { 372 | $from = DPSAdapter::get_receipt_from(); 373 | if ($from) { 374 | $body = $this->renderWith($this->ClassName."_receipt"); 375 | $body .= $member->ReceiptMessage(); 376 | $email = new Email($from, $member->Email, "Payment receipt (Ref no. #".$this->ID.")", $body); 377 | $email->send(); 378 | } 379 | } 380 | } 381 | 382 | protected function parsedResponseXML($cached = true) 383 | { 384 | if (!$this->cacheResponseXML || !$cached) { 385 | $this->cacheResponseXML = simplexml_load_string($this->ResponseXML, 'SimpleXMLElement', LIBXML_NOWARNING); 386 | } 387 | return $this->cacheResponseXML; 388 | } 389 | 390 | /** 391 | * From the ResponseXML, retrieve the AmountSettlement value. 392 | * CAUTION: Only works for transactions created through PXPay, not PXPost! 393 | * 394 | * @return bool|string 395 | */ 396 | public function getAmountSettlement() 397 | { 398 | $xml = $this->parsedResponseXML(); 399 | return ($xml) ? (string) $xml->AmountSettlement : false; 400 | } 401 | 402 | /** 403 | * From the ResponseXML, retrieve the CardName value. 404 | * @return bool|string 405 | */ 406 | public function getCardName() 407 | { 408 | $xml = $this->parsedResponseXML(); 409 | if (!$xml) { 410 | return false; 411 | } 412 | return ($xml->Transaction) ? (string)$xml->Transaction->CardName : (string)$xml->CardName; 413 | } 414 | 415 | /** 416 | * From the ResponseXML, retrieve the CardHolderName value. 417 | * @return bool|string 418 | */ 419 | public function getCardHolderName() 420 | { 421 | $xml = $this->parsedResponseXML(); 422 | if (!$xml) { 423 | return false; 424 | } 425 | return ($xml->Transaction) ? (string)$xml->Transaction->CardHolderName : (string)$xml->CardHolderName; 426 | } 427 | 428 | /** 429 | * From the ResponseXML, retrieve the DateExpiry value. 430 | * @return bool|string 431 | */ 432 | public function getDateExpiry() 433 | { 434 | $xml = $this->parsedResponseXML(); 435 | if (!$xml) { 436 | return false; 437 | } 438 | return ($xml->Transaction) ? (string)$xml->Transaction->DateExpiry : (string)$xml->DateExpiry; 439 | } 440 | 441 | /** 442 | * From the ResponseXML, retrieve the CardNumber value. 443 | * @return bool|string 444 | */ 445 | public function getCardNumber() 446 | { 447 | $xml = $this->parsedResponseXML(); 448 | if (!$xml) { 449 | return false; 450 | } 451 | return ($xml->Transaction) ? (string)$xml->Transaction->CardNumber : (string)$xml->CardNumber; 452 | } 453 | 454 | protected $dpsAdapter; 455 | 456 | public function getDPSAdapter() 457 | { 458 | if (!$this->dpsAdapter) { 459 | $this->dpsAdapter = new DPSAdapter(); 460 | } 461 | return $this->dpsAdapter; 462 | } 463 | 464 | public function setDPSAdapter($adapter) 465 | { 466 | $this->dpsAdapter = $adapter; 467 | } 468 | } 469 | -------------------------------------------------------------------------------- /code/DPSPayment/DPSRecurringPayment.php: -------------------------------------------------------------------------------- 1 | 'Text', 9 | 'AuthCode' => 'Varchar(22)', 10 | 'MerchantReference' => 'Varchar(64)', 11 | 'DPSHostedRedirectURL' => 'Text', 12 | 'DPSBillingID' => "Varchar(16)", 13 | 'AuthAmount' => 'Decimal', 14 | 15 | // We store the whole raw response xml in case that tracking back the payment is needed in a later stage for whatever the reason. 16 | 'ResponseXML' => "Text", 17 | ); 18 | 19 | private static $input_elements = array( 20 | 'Amount', 21 | 'CardHolderName', 22 | 'CardNumber', 23 | 'BillingId', 24 | 'Cvc2', 25 | 'DateExpiry', 26 | 'DpsBillingId', 27 | 'DpsTxnRef', 28 | 'EnableAddBillCard', 29 | 'InputCurrency', 30 | 'MerchantReference', 31 | 'PostUsername', 32 | 'PostPassword', 33 | 'TxnType', 34 | 'TxnData1', 35 | 'TxnData2', 36 | 'TxnData3', 37 | 'TxnId', 38 | 'EnableAvsData', 39 | 'AvsAction', 40 | 'AvsPostCode', 41 | 'AvsStreetAddress', 42 | 'DateStart', 43 | 'IssueNumber', 44 | 'Track2', 45 | ); 46 | 47 | private static $dpshosted_input_elements = array( 48 | 'PxPayUserId', 49 | 'PxPayKey', 50 | 'AmountInput', 51 | 'CurrencyInput', 52 | 'EmailAddress', 53 | 'EnableAddBillCard', 54 | 'MerchantReference', 55 | 'TxnData1', 56 | 'TxnData2', 57 | 'TxnData3', 58 | 'TxnType', 59 | 'TxnId', 60 | 'UrlFail', 61 | 'UrlSuccess', 62 | ); 63 | 64 | public static $default_sort = "ID DESC"; 65 | 66 | public function recurringAuth($data) 67 | { 68 | DB::getConn()->transactionStart(); 69 | try { 70 | $this->TxnType = "Auth"; 71 | $this->AuthAmount = 1.00; 72 | $this->write(); 73 | $adapter = new DPSAdapter(); 74 | $inputs = $this->prepareDPSHostedRecurringAuthRequest($data); 75 | $adapter->doDPSHostedPayment($inputs, $this); 76 | } catch (Exception $e) { 77 | DB::getConn()->transactionRollback(); 78 | $this->handleError($e); 79 | } 80 | } 81 | 82 | public function prepareDPSHostedRecurringAuthRequest($data) 83 | { 84 | //never put this loop after $inputs['AmountInput'] = $amount, since it will change it to an array. 85 | foreach ($data as $element => $value) { 86 | if (in_array($element, self::$dpshosted_input_elements)) { 87 | $inputs[$element] = $value; 88 | } 89 | } 90 | 91 | $inputs['TxnData1'] = $this->ID; 92 | $inputs['TxnType'] = 'Auth'; 93 | $inputs['EnableAddBillCard'] = 1; 94 | $inputs['AmountInput'] = $this->AuthAmount; 95 | $inputs['InputCurrency'] = $this->Amount->Currency; 96 | $inputs['MerchantReference'] = $this->MerchantReference; 97 | 98 | $postProcess_url = Director::absoluteBaseURL() ."DPSAdapter/processDPSHostedResponse"; 99 | $inputs['UrlFail'] = $postProcess_url; 100 | $inputs['UrlSuccess'] = $postProcess_url; 101 | 102 | return $inputs; 103 | } 104 | 105 | public function merchantRecurringAuth($data) 106 | { 107 | DB::getConn()->transactionStart(); 108 | try { 109 | $this->AuthAmount = 1.00; 110 | $this->write(); 111 | 112 | $adapter = new DPSAdapter(); 113 | $inputs = $this->prepareMerchantHostedRecurringAuthInputs($data); 114 | $adapter->doPayment($inputs, $this); 115 | DB::getConn()->transactionEnd(); 116 | } catch (Exception $e) { 117 | DB::getConn()->transactionRollback(); 118 | $this->handleError($e); 119 | } 120 | } 121 | 122 | public function prepareMerchantHostedRecurringAuthInputs($data) 123 | { 124 | //never put this loop after $inputs['AmountInput'] = $this->Amount->Amount;, since it will change it to an array. 125 | foreach ($data as $element => $value) { 126 | if (in_array($element, self::$input_elements)) { 127 | $inputs[$element] = $value; 128 | } 129 | } 130 | $inputs['TxnData1'] = $this->ID; 131 | $inputs['TxnType'] = 'Validate'; 132 | $inputs['EnableAddBillCard'] = 1; 133 | $inputs['Amount'] = $this->AuthAmount; 134 | $inputs['InputCurrency'] = $this->Amount->Currency; 135 | $inputs['MerchantReference'] = $this->MerchantReference; 136 | //special element 137 | $inputs['CardNumber'] = implode('', $data['CardNumber']); 138 | return $inputs; 139 | } 140 | 141 | public function getNextPayment() 142 | { 143 | $next = parent::getNextPayment(); 144 | $next->ClassName = 'DPSPayment'; 145 | $next->RecordClassName = 'DPSPayment'; 146 | $next->TxnType = 'Purchase'; 147 | $next->MerchantReference = $this->MerchantReference; 148 | $next->write(); 149 | return DataObject::get_by_id('DPSPayment', $next->ID); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /code/DPSPayment/thirdparty/dps_hosted_helper/LICENSE: -------------------------------------------------------------------------------- 1 | See http://www.paymentexpress.com/Technical_Resources/Sample_code_-_PHP -------------------------------------------------------------------------------- /code/DPSPayment/thirdparty/dps_hosted_helper/MifMessage.php: -------------------------------------------------------------------------------- 1 | xml_ = $xml; 31 | $this->xml_value_ = $value; 32 | $this->xml_index_ = $index; 33 | } 34 | #print_r($this->xml_value_); # JH_DEBUG 35 | } 36 | 37 | # Return the value of the specified top-level attribute. 38 | # This method can only return attributes of the root element. 39 | # If the attribute is not found, return "". 40 | public function get_attribute($attribute) 41 | { 42 | #$attribute = strtoupper($attribute); 43 | $attributes = $this->xml_value_[0]["attributes"]; 44 | return $attributes[$attribute]; 45 | } 46 | 47 | # Return the text of the specified element. 48 | # The element is given as a simplified XPath-like name. 49 | # For example, "Link/ServerOk" refers to the ServerOk element 50 | # nested in the Link element (nested in the root element). 51 | # If the element is not found, return "". 52 | public function get_element_text($element) 53 | { 54 | #print_r($this->xml_value_); # JH_DEBUG 55 | $index = $this->get_element_index($element, 0); 56 | if ($index == 0) { 57 | return ""; 58 | } else { 59 | ## TW2004-09-24: Fixed bug when elemnt existent but empty 60 | # 61 | $elementObj = $this->xml_value_[$index]; 62 | if (! array_key_exists("value", $elementObj)) { 63 | return ""; 64 | } 65 | 66 | return $this->xml_value_[$index]["value"]; 67 | } 68 | } 69 | 70 | # (internal method) 71 | # Return the index of the specified element, 72 | # relative to some given root element index. 73 | # 74 | public function get_element_index($element, $rootindex = 0) 75 | { 76 | #$element = strtoupper($element); 77 | $pos = strpos($element, "/"); 78 | if ($pos !== false) { 79 | # element contains '/': find first part 80 | $start_path = substr($element, 0, $pos); 81 | $remain_path = substr($element, $pos+1); 82 | $index = $this->get_element_index($start_path, $rootindex); 83 | if ($index == 0) { 84 | # couldn't find first part; give up. 85 | return 0; 86 | } 87 | # recursively find rest 88 | return $this->get_element_index($remain_path, $index); 89 | } else { 90 | # search from the parent across all its children 91 | # i.e. until we get the parent's close tag. 92 | $level = $this->xml_value_[$rootindex]["level"]; 93 | if ($this->xml_value_[$rootindex]["type"] == "complete") { 94 | return 0; # no children 95 | } 96 | $index = $rootindex+1; 97 | while ($indexxml_value_) && 98 | !($this->xml_value_[$index]["level"]==$level && 99 | $this->xml_value_[$index]["type"]=="close")) { 100 | # if one below parent and tag matches, bingo 101 | if ($this->xml_value_[$index]["level"] == $level+1 && 102 | # $this->xml_value_[$index]["type"] == "complete" && 103 | $this->xml_value_[$index]["tag"] == $element) { 104 | return $index; 105 | } 106 | $index++; 107 | } 108 | return 0; 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /code/DPSPayment/thirdparty/dps_hosted_helper/PxAccess.php: -------------------------------------------------------------------------------- 1 | Mac_Key = pack("H*", $Mac_Key); 11 | $this->Des_Key = pack("H*", $Des_Key); 12 | $this->PxAccess_Url = $Url; 13 | $this->PxAccess_Userid = $UserId; 14 | } 15 | public function makeRequest($request) 16 | { 17 | #Validate the REquest 18 | if ($request->validData() == false) { 19 | return "" ; 20 | } 21 | 22 | #$txnId=rand(1,100000); 23 | //$txnId = uniqid("MI"); #You need to generate you own unqiue reference. JZ:2004-08-12 24 | //$request->setTxnId($txnId); 25 | $request->setTs($this->getCurrentTS()); 26 | $request->setSwVersion("2.01.01"); 27 | $request->setAppletType("PHPPxAccess"); 28 | 29 | 30 | $xml = $request->toXml(); 31 | 32 | if (strlen($xml)%8 != 0) { 33 | $xml = str_pad($xml, strlen($xml) + 8-strlen($xml)%8); # pad to multiple of 8 34 | } 35 | #add MAC code JZ2004-8-16 36 | $mac = $this->makeMAC($xml, $this->Mac_Key); 37 | $msg = $xml.$mac; 38 | #$msg = $xml; 39 | $enc = $this->encrypt_tripledes($msg, $this->Des_Key); #JZ2004-08-16: Include the MAC code 40 | 41 | $enclen = strlen($enc) * 2; 42 | 43 | $enc_hex = unpack("H$enclen", $enc); #JZ2005-03-14: there is a bug in the new version php unpack function 44 | #$enc_hex = @unpack("H*", $enc); #JZ2005-03-14: there is a bug in the new version php unpack function 45 | 46 | #$enc_hex = $enc_hex[""]; #use this function if PHP version before 4.3.4 47 | #$enc_hex = $enc_hex[1]; #use this function if PHP version after 4.3.4 48 | $enc_hex = (version_compare(PHP_VERSION, "4.3.4", ">=")) ? $enc_hex[1] :$enc_hex[""]; 49 | 50 | $PxAccess_Redirect = "$this->PxAccess_Url?userid=$this->PxAccess_Userid&request=$enc_hex"; 51 | 52 | return $PxAccess_Redirect; 53 | } 54 | 55 | #****************************************************************************** 56 | # This function ecrypts data using 3DES via libmcrypt 57 | #****************************************************************************** 58 | public function encrypt_tripledes($data, $key) 59 | { 60 | # deprecated libmcrypt 2.2 encryption: use this if you have libmcrypt 2.2.x 61 | # $result = mcrypt_ecb(MCRYPT_DES, $key, $data, MCRYPT_ENCRYPT); 62 | # return $result; 63 | # 64 | # otherwise use this for libmcrypt 2.4.x and above: 65 | $td = mcrypt_module_open('tripledes', '', 'ecb', ''); 66 | $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND); 67 | mcrypt_generic_init($td, $key, $iv); 68 | $result = mcrypt_generic($td, $data); 69 | #mcrypt_generic_deinit($td); #Might cause problem in some PHP version 70 | return $result; 71 | } 72 | 73 | 74 | #****************************************************************************** 75 | # This function decrypts data using 3DES via libmcrypt 76 | #****************************************************************************** 77 | public function decrypt_tripledes($data, $key) 78 | { 79 | # deprecated libmcrypt 2.2 encryption: use this if you have libmcrypt 2.2.x 80 | # $result = mcrypt_ecb(MCRYPT_DES, $key, $data, MCRYPT_DECRYPT); 81 | # return $result; 82 | # 83 | # otherwise use this for libmcrypt 2.4.x and above: 84 | $td = mcrypt_module_open('tripledes', '', 'ecb', ''); 85 | $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND); 86 | mcrypt_generic_init($td, $key, $iv); 87 | $result = mdecrypt_generic($td, $data); 88 | #mcrypt_generic_deinit($td); #Might cause problem in some PHP version 89 | return $result; 90 | } 91 | 92 | #JZ2004-08-16 93 | 94 | #****************************************************************************** 95 | # Generate and return a message authentication code (MAC) for a string. 96 | # (Uses ANSI X9.9 procedure.) 97 | #****************************************************************************** 98 | public function makeMAC($msg, $Mackey) 99 | { 100 | if (strlen($msg)%8 != 0) { 101 | $extra = 8 - strlen($msg)%8; 102 | $msg .= str_repeat(" ", $extra); # pad to multiple of 8 103 | } 104 | $mac = pack("C*", 0, 0, 0, 0, 0, 0, 0, 0); # start with all zeros 105 | #$mac_result = unpack("C*", $mac); 106 | 107 | for ($i=0; $iencrypt_des($mac, $Mackey); 112 | } 113 | #$mac = pack("C*", $mac); 114 | #$mac_result= encrypt_des($mac, $Mackey); 115 | 116 | $mac_result = unpack("H8", $mac); 117 | #$mac_result = $mac_result[""]; #use this function if PHP version before 4.3.4 118 | #$mac_result = $mac_result[1]; #use this function if PHP version after 4.3.4 119 | $mac_result = (version_compare(PHP_VERSION, "4.3.4", ">=")) ? $mac_result[1]: $mac_result[""]; 120 | 121 | return $mac_result; 122 | } 123 | 124 | #****************************************************************************** 125 | # This function ecrypts data using DES via libmcrypt 126 | # JZ2004-08-16 127 | #****************************************************************************** 128 | public function encrypt_des($data, $key) 129 | { 130 | # deprecated libmcrypt 2.2 encryption: use this if you have libmcrypt 2.2.x 131 | # $result = mcrypt_ecb(MCRYPT_3DES, $key, $data, MCRYPT_ENCRYPT); 132 | # return $result; 133 | # 134 | # otherwise use this for libmcrypt 2.4.x and above: 135 | 136 | $td = mcrypt_module_open('des', '', 'ecb', ''); 137 | $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND); 138 | mcrypt_generic_init($td, $key, $iv); 139 | $result = mcrypt_generic($td, $data); 140 | #mcrypt_generic_deinit($td); #Might cause problem in some PHP version 141 | mcrypt_module_close($td); 142 | 143 | return $result; 144 | } 145 | 146 | 147 | #JZ2004-08-16 148 | public function getResponse($resp_enc) 149 | { 150 | #global $Mac_Key; 151 | $enc = pack("H*", $resp_enc); 152 | $resp = trim($this->decrypt_tripledes($enc, $this->Des_Key)); 153 | $xml = substr($resp, 0, strlen($resp)-8); 154 | $mac = substr($resp, -8); 155 | $checkmac = $this->makeMac($xml, $this->Mac_Key); 156 | if ($mac != $checkmac) { 157 | $xml = "0Response MAC Invalid"; 158 | } 159 | 160 | $pxresp = new PxPayResponse($xml); 161 | return $pxresp; 162 | } 163 | 164 | 165 | 166 | #****************************************************************************** 167 | # Return the current time (GMT/UTC).The return time formatted YYYYMMDDHHMMSS. 168 | #JZ2004-08-30 169 | #****************************************************************************** 170 | public function getCurrentTS() 171 | { 172 | return gmstrftime("%Y%m%d%H%M%S", time()); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /code/DPSPayment/thirdparty/dps_hosted_helper/PxPay.php: -------------------------------------------------------------------------------- 1 | PxPay_Key = $Key;#pack("H*", $Key); 11 | $this->PxPay_Url = $Url; 12 | $this->PxPay_Userid = $UserId; 13 | } 14 | 15 | #****************************************************************************** 16 | # Create an encoded request for the PxPay Host. 17 | #****************************************************************************** 18 | public function makeRequest($request) 19 | { 20 | if ($request->validData() == false) { 21 | return "" ; 22 | } 23 | 24 | $request->setTs($this->getCurrentTS()); 25 | $request->setSwVersion("1.0"); 26 | $request->setAppletType("PHPPxPay"); 27 | $request->setUserId($this->PxPay_Userid); 28 | $request->setKey($this->PxPay_Key); 29 | $xml = $request->toXml(); 30 | 31 | $ch = curl_init(); 32 | curl_setopt($ch, CURLOPT_URL, $this->PxPay_Url); 33 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 34 | curl_setopt($ch, CURLOPT_POST, true); 35 | curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); 36 | 37 | $response = curl_exec($ch); 38 | 39 | if (curl_error($ch)) { 40 | throw new Exception(curl_error($ch)); 41 | } 42 | 43 | return $response; 44 | } 45 | 46 | #****************************************************************************** 47 | # Return the decoded response from the PxPay Host. 48 | #****************************************************************************** 49 | public function getResponse($resp_enc) 50 | { 51 | $xml = "".$this->PxPay_Userid."".$this->PxPay_Key."".$resp_enc.""; 52 | 53 | $ch = curl_init(); 54 | curl_setopt($ch, CURLOPT_URL, $this->PxPay_Url); 55 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 56 | curl_setopt($ch, CURLOPT_POST, true); 57 | curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); 58 | 59 | $response = curl_exec($ch); 60 | 61 | if (curl_error($ch)) { 62 | throw new Exception(curl_error($ch)); 63 | } 64 | 65 | return new PxPayResponse($response); 66 | } 67 | 68 | #****************************************************************************** 69 | # Return the current time (GMT/UTC).The return time formatted YYYYMMDDHHMMSS. 70 | #****************************************************************************** 71 | public function getCurrentTS() 72 | { 73 | return gmstrftime("%Y%m%d%H%M%S", time()); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /code/DPSPayment/thirdparty/dps_hosted_helper/PxPayMessage.php: -------------------------------------------------------------------------------- 1 | DpsTxnRef = $DpsTxnRef; 25 | } 26 | 27 | public function getDpsTxnRef() 28 | { 29 | return $this->DpsTxnRef; 30 | } 31 | 32 | public function setDpsBillingId($DpsBillingId) 33 | { 34 | $this->DpsBillingId = $DpsBillingId; 35 | } 36 | 37 | public function getDpsBillingId() 38 | { 39 | return $this->DpsBillingId; 40 | } 41 | public function setBillingId($BillingId) 42 | { 43 | $this->BillingId = $BillingId; 44 | } 45 | 46 | public function getBillingId() 47 | { 48 | return $this->BillingId; 49 | } 50 | public function setTxnType($TxnType) 51 | { 52 | $this->TxnType = $TxnType; 53 | } 54 | public function getTxnType() 55 | { 56 | return $this->TxnType; 57 | } 58 | public function setMerchantReference($MerchantReference) 59 | { 60 | $this->MerchantReference = $MerchantReference; 61 | } 62 | 63 | public function getMerchantReference() 64 | { 65 | return $this->MerchantReference; 66 | } 67 | public function setEmailAddress($EmailAddress) 68 | { 69 | $this->EmailAddress = $EmailAddress; 70 | } 71 | 72 | public function getEmailAddress() 73 | { 74 | return $this->EmailAddress; 75 | } 76 | 77 | public function setTxnData1($TxnData1) 78 | { 79 | $this->TxnData1 = $TxnData1; 80 | } 81 | public function getTxnData1() 82 | { 83 | return $this->TxnData1; 84 | } 85 | public function setTxnData2($TxnData2) 86 | { 87 | $this->TxnData2 = $TxnData2; 88 | } 89 | public function getTxnData2() 90 | { 91 | return $this->TxnData2; 92 | } 93 | 94 | public function getTxnData3() 95 | { 96 | return $this->TxnData3; 97 | } 98 | public function setTxnData3($TxnData3) 99 | { 100 | $this->TxnData3 = $TxnData3; 101 | } 102 | public function toXml() 103 | { 104 | $arr = get_object_vars($this); 105 | $root = strtolower(get_class($this)); 106 | #echo "
root:".$root; 107 | if ($root == "pxpayrequest") { 108 | $root = "GenerateRequest"; 109 | } elseif ($root == "pxpayresponse") { 110 | $root = "Response"; 111 | } else { 112 | $root ="Request"; 113 | } 114 | 115 | 116 | $xml = "<$root>"; 117 | while (list($prop, $val) = each($arr)) { 118 | $xml .= "<$prop>$val" ; 119 | } 120 | 121 | $xml .= ""; 122 | 123 | return $xml; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /code/DPSPayment/thirdparty/dps_hosted_helper/PxPayRequest.php: -------------------------------------------------------------------------------- 1 | PxPayMessage(); 20 | } 21 | 22 | public function setAppletType($AppletType) 23 | { 24 | $this->AppletType = $AppletType; 25 | } 26 | 27 | public function getAppletType() 28 | { 29 | return $this->AppletType; 30 | } 31 | 32 | 33 | 34 | public function setTs($Ts) 35 | { 36 | $this->TS = $Ts; 37 | } 38 | public function setEnableAddBillCard($EnableBillAddCard) 39 | { 40 | $this->EnableAddBillCard = $EnableBillAddCard; 41 | } 42 | 43 | public function getEnableAddBillCard() 44 | { 45 | return $this->EnableAddBillCard; 46 | } 47 | public function setInputCurrency($InputCurrency) 48 | { 49 | $this->CurrencyInput = $InputCurrency; 50 | } 51 | public function getInputCurrency() 52 | { 53 | return $this->CurrencyInput; 54 | } 55 | public function setTxnId($TxnId) 56 | { 57 | $this->TxnId = $TxnId; 58 | } 59 | public function getTxnId() 60 | { 61 | return $this->TxnId; 62 | } 63 | 64 | public function setUrlFail($UrlFail) 65 | { 66 | $this->UrlFail = $UrlFail; 67 | } 68 | public function getUrlFail() 69 | { 70 | return $this->UrlFail; 71 | } 72 | public function setUrlSuccess($UrlSuccess) 73 | { 74 | $this->UrlSuccess = $UrlSuccess; 75 | } 76 | public function setAmountInput($AmountInput) 77 | { 78 | $this->AmountInput = sprintf("%9.2f", $AmountInput); 79 | } 80 | 81 | public function getAmountInput() 82 | { 83 | return $this->AmountInput; 84 | } 85 | public function setUserId($UserId) 86 | { 87 | $this->PxPayUserId = $UserId; 88 | } 89 | 90 | public function setKey($Key) 91 | { 92 | $this->PxPayKey = $Key; 93 | } 94 | 95 | public function setOpt($timeOut) 96 | { 97 | if ($timeOut) { 98 | $this->Opt = 'TO='.gmdate('ymdHi', $timeOut->format('U')); 99 | } 100 | } 101 | 102 | public function setSwVersion($SwVersion) 103 | { 104 | $this->AppletVersion = $SwVersion; 105 | } 106 | 107 | public function getSwVersion() 108 | { 109 | return $this->AppletVersion; 110 | } 111 | #****************************************************************** 112 | #Data validation 113 | #****************************************************************** 114 | public function validData() 115 | { 116 | $msg = ""; 117 | if ($this->TxnType != "Purchase") { 118 | if ($this->TxnType != "Auth") { 119 | if ($this->TxnType != "GetCurrRate") { 120 | if ($this->TxnType != "Refund") { 121 | if ($this->TxnType != "Complete") { 122 | if ($this->TxnType != "Order1") { 123 | $msg = "Invalid TxnType[$this->TxnType]
"; 124 | } 125 | } 126 | } 127 | } 128 | } 129 | } 130 | 131 | if (strlen($this->MerchantReference) > 64) { 132 | $msg = "Invalid MerchantReference [$this->MerchantReference]
"; 133 | } 134 | 135 | if (strlen($this->TxnId) > 16) { 136 | $msg = "Invalid TxnId [$this->TxnId]
"; 137 | } 138 | if (strlen($this->TxnData1) > 255) { 139 | $msg = "Invalid TxnData1 [$this->TxnData1]
"; 140 | } 141 | if (strlen($this->TxnData2) > 255) { 142 | $msg = "Invalid TxnData2 [$this->TxnData2]
"; 143 | } 144 | if (strlen($this->TxnData3) > 255) { 145 | $msg = "Invalid TxnData3 [$this->TxnData3]
"; 146 | } 147 | 148 | if (strlen($this->EmailAddress) > 255) { 149 | $msg = "Invalid EmailAddress [$this->EmailAddress]
"; 150 | } 151 | 152 | if (strlen($this->UrlFail) > 255) { 153 | $msg = "Invalid UrlFail [$this->UrlFail]
"; 154 | } 155 | if (strlen($this->UrlSuccess) > 255) { 156 | $msg = "Invalid UrlSuccess [$this->UrlSuccess]
"; 157 | } 158 | if (strlen($this->BillingId) > 32) { 159 | $msg = "Invalid BillingId [$this->BillingId]
"; 160 | } 161 | if (strlen($this->DpsBillingId) > 16) { 162 | $msg = "Invalid DpsBillingId [$this->DpsBillingId]
"; 163 | } 164 | 165 | if ($msg != "") { 166 | trigger_error($msg, E_USER_ERROR); 167 | return false; 168 | } 169 | return true; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /code/DPSPayment/thirdparty/dps_hosted_helper/PxPayResponse.php: -------------------------------------------------------------------------------- 1 | PxPayMessage(); 29 | 30 | 31 | $this->setBillingId($msg->get_element_text("BillingId")); 32 | $this->setDpsBillingId($msg->get_element_text("DpsBillingId")); 33 | $this->setEmailAddress($msg->get_element_text("EmailAddress")); 34 | $this->setMerchantReference($msg->get_element_text("MerchantReference")); 35 | $this->setTxnData1($msg->get_element_text("TxnData1")); 36 | $this->setTxnData2($msg->get_element_text("TxnData2")); 37 | $this->setTxnData3($msg->get_element_text("TxnData3")); 38 | $this->setTxnType($msg->get_element_text("TxnType")); 39 | $this->Success = $msg->get_element_text("Success"); 40 | $this->AuthCode = $msg->get_element_text("AuthCode"); 41 | $this->AmountSettlement = $msg->get_element_text("AmountSettlement"); 42 | $this->CurrencySettlement = $msg->get_element_text("CurrencySettlement"); 43 | $this->CardName = $msg->get_element_text("CardName"); 44 | $this->ResponseText = $msg->get_element_text("ResponseText"); 45 | $this->DpsTxnRef = $msg->get_element_text("DpsTxnRef"); 46 | $this->MerchantTxnId = $msg->get_element_text("TxnId"); 47 | $this->CardHolderName = $msg->get_element_text("CardHolderName"); 48 | $this->CardNumber = $msg->get_element_text("CardNumber"); 49 | $this->DateExpiry = $msg->get_element_text("DateExpiry"); 50 | $this->CurrencyInput = $msg->get_element_text("CurrencyInput"); 51 | } 52 | public function getTS() 53 | { 54 | return $this->TS; 55 | } 56 | public function getMerchantTxnId() 57 | { 58 | return $this->MerchantTxnId; 59 | } 60 | 61 | public function getResponseText() 62 | { 63 | return $this->ResponseText; 64 | } 65 | public function getUserId() 66 | { 67 | return $this->UserId; 68 | } 69 | public function getCurrencyInput() 70 | { 71 | return $this->CurrencyInput; 72 | } 73 | public function getCardName() 74 | { 75 | return $this->CardName; 76 | } 77 | public function getCardNumber() 78 | { 79 | return $this->CardNumber; 80 | } 81 | public function getCardHolderName() 82 | { 83 | return $this->CardHolderName; 84 | } 85 | public function getDateExpiry() 86 | { 87 | return $this->DateExpiry; 88 | } 89 | public function getCurrencySettlement() 90 | { 91 | $this->CurrencySettlement; 92 | } 93 | public function getAmountSettlement() 94 | { 95 | return $this->AmountSettlement; 96 | } 97 | public function getSuccess() 98 | { 99 | return $this->Success; 100 | } 101 | public function getStatusRequired() 102 | { 103 | return $this->StatusRequired; 104 | } 105 | public function getRetry() 106 | { 107 | return $this->Retry; 108 | } 109 | public function getAuthCode() 110 | { 111 | return $this->AuthCode; 112 | } 113 | #****************************************************************************** 114 | # Return the expired time, i.e. 2 days ago (GMT/UTC). 115 | #JZ2004-08-30 116 | #****************************************************************************** 117 | public function getExpiredTS() 118 | { 119 | return gmstrftime("%Y%m%d%H%M%S", time()- 2 * 24 * 60 * 60); 120 | } 121 | 122 | public function getTxnId() 123 | { 124 | return $this->MerchantTxnId; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /code/Eway/EwayConfig.inc.php: -------------------------------------------------------------------------------- 1 | sets to testing mode, to live mode 24 | 25 | $eWAY_CustomerID = "87654321"; // Set this to your eWAY Customer ID 26 | $eWAY_PaymentMethod = REAL_TIME; // Set this to the payment gatway you would like to use (REAL_TIME, REAL_TIME_CVN or GEO_IP_ANTI_FRAUD) 27 | $eWAY_UseLive = false; // Set this to true to use the live gateway 28 | -------------------------------------------------------------------------------- /code/Eway/EwayPaymentLive.php: -------------------------------------------------------------------------------- 1 | myCustomerID = $customerID; 15 | switch ($method) { 16 | 17 | case "REAL_TIME"; 18 | 19 | if ($liveGateway) { 20 | $this->myGatewayURL = EWAY_PAYMENT_LIVE_REAL_TIME; 21 | } else { 22 | $this->myGatewayURL = EWAY_PAYMENT_LIVE_REAL_TIME_TESTING_MODE; 23 | } 24 | break; 25 | case "REAL_TIME_CVN"; 26 | if ($liveGateway) { 27 | $this->myGatewayURL = EWAY_PAYMENT_LIVE_REAL_TIME_CVN; 28 | } else { 29 | $this->myGatewayURL = EWAY_PAYMENT_LIVE_REAL_TIME_CVN_TESTING_MODE; 30 | } 31 | break; 32 | case "GEO_IP_ANTI_FRAUD"; 33 | if ($liveGateway) { 34 | $this->myGatewayURL = EWAY_PAYMENT_LIVE_GEO_IP_ANTI_FRAUD; 35 | } else { 36 | //in testing mode process with REAL-TIME 37 | $this->myGatewayURL = EWAY_PAYMENT_LIVE_GEO_IP_ANTI_FRAUD_TESTING_MODE; 38 | } 39 | break; 40 | } 41 | } 42 | 43 | 44 | //Payment Function 45 | public function doPayment() 46 | { 47 | $xmlRequest = "" . $this->myCustomerID . ""; 48 | foreach ($this->myTransactionData as $key=>$value) { 49 | $xmlRequest .= "<$key>$value"; 50 | } 51 | $xmlRequest .= ""; 52 | 53 | $xmlResponse = $this->sendTransactionToEway($xmlRequest); 54 | 55 | if ($xmlResponse!="") { 56 | $responseFields = $this->parseResponse($xmlResponse); 57 | return $responseFields; 58 | } else { 59 | die("Error in XML response from eWAY: " + $xmlResponse); 60 | } 61 | } 62 | 63 | //Send XML Transaction Data and receive XML response 64 | public function sendTransactionToEway($xmlRequest) 65 | { 66 | $ch = curl_init($this->myGatewayURL); 67 | curl_setopt($ch, CURLOPT_POST, 1); 68 | curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlRequest); 69 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 70 | foreach ($this->myCurlPreferences as $key=>$value) { 71 | curl_setopt($ch, $key, $value); 72 | } 73 | 74 | $xmlResponse = curl_exec($ch); 75 | 76 | if (curl_errno($ch) == CURLE_OK) { 77 | return $xmlResponse; 78 | } 79 | } 80 | 81 | 82 | //Parse XML response from eway and place them into an array 83 | public function parseResponse($xmlResponse) 84 | { 85 | $xml_parser = xml_parser_create(); 86 | xml_parse_into_struct($xml_parser, $xmlResponse, $xmlData, $index); 87 | $responseFields = array(); 88 | foreach ($xmlData as $data) { 89 | if ($data["level"] == 2) { 90 | $responseFields[$data["tag"]] = $data["value"]; 91 | } 92 | } 93 | return $responseFields; 94 | } 95 | 96 | 97 | //Set Transaction Data 98 | //Possible fields: "TotalAmount", "CustomerFirstName", "CustomerLastName", "CustomerEmail", "CustomerAddress", "CustomerPostcode", "CustomerInvoiceDescription", "CustomerInvoiceRef", 99 | //"CardHoldersName", "CardNumber", "CardExpiryMonth", "CardExpiryYear", "TrxnNumber", "Option1", "Option2", "Option3", "CVN", "CustomerIPAddress", "CustomerBillingCountry" 100 | public function setTransactionData($field, $value) 101 | { 102 | //if($field=="TotalAmount") 103 | // $value = round($value*100); 104 | $this->myTransactionData["eway" . $field] = htmlentities(trim($value)); 105 | } 106 | 107 | 108 | //receive special preferences for Curl 109 | public function setCurlPreferences($field, $value) 110 | { 111 | $this->myCurlPreferences[$field] = $value; 112 | } 113 | 114 | 115 | //obtain visitor IP even if is under a proxy 116 | public function getVisitorIP() 117 | { 118 | $ip = $_SERVER["REMOTE_ADDR"]; 119 | $proxy = $_SERVER["HTTP_X_FORWARDED_FOR"]; 120 | if (ereg("^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$", $proxy)) { 121 | $ip = $_SERVER["HTTP_X_FORWARDED_FOR"]; 122 | } 123 | return $ip; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /code/Eway/EwayXMLPayment.php: -------------------------------------------------------------------------------- 1 | '; 48 | $privacyLink = '' . $logo . '
'; 49 | $fields = new FieldSet( 50 | new LiteralField('EwayInfo', $privacyLink), 51 | new LiteralField( 52 | 'EwayXMLPaymentsList', 53 | 'Visa' . 54 | 'MasterCard' . 55 | 'American Express' . 56 | 'Dinners Club' . 57 | 'JCB' 58 | ), 59 | new TextField('Eway_CreditCardHolderName', 'Credit Card Holder Name :') 60 | ); 61 | if (self::$test_mode) { 62 | $fields->push(new ReadonlyField('Eway_CreditCardNumber', 'Credit Card Number :', self::$test_credit_card_number)); 63 | } else { 64 | $fields->push(new CreditCardField('Eway_CreditCardNumber', 'Credit Card Number :')); 65 | } 66 | $fields->push(new TextField('Eway_CreditCardExpiry', 'Credit Card Expiry : (MMYY)', '', 4)); 67 | $fields->push(new DropdownField( 68 | 'Eway_CreditCardType', 69 | 'Credit Card Type :', 70 | array( 71 | 'VISA' => 'Visa', 72 | 'MASTERCARD' => 'MasterCard', 73 | 'AMEX' => 'Amex', 74 | 'DINERS' => 'Diners', 75 | 'JCB' => 'JCB' 76 | ) 77 | )); 78 | if (self::$cvn_mode) { 79 | $fields->push(new TextField('Eway_CreditCardCVN', 'Credit Card CVN : (3 or 4 digits)', '', 4)); 80 | } 81 | return $fields; 82 | } 83 | 84 | public function getPaymentFormRequirements() 85 | { 86 | $jsCode = << $jsCode, 'php' => $phpCode); 107 | } 108 | 109 | public function processPayment($data, $form) 110 | { 111 | 112 | // 1) Main Informations 113 | 114 | $member = $this->Member(); 115 | 116 | // 2) Mandatory Settings 117 | 118 | $inputs['CardHoldersName'] = $data['Eway_CreditCardHolderName']; 119 | $inputs['CardNumber'] = self::$test_mode ? self::$test_credit_card_number : implode('', $data['Eway_CreditCardNumber']); 120 | $inputs['CardExpiryMonth'] = substr($data['Eway_CreditCardExpiry'], 0, 2); 121 | $inputs['CardExpiryYear'] = substr($data['Eway_CreditCardExpiry'], 2, 2); 122 | if (self::$cvn_mode) { 123 | $inputs['CVN'] = $data['Eway_CreditCardCVN']; 124 | } 125 | $inputs['TotalAmount'] = self::$test_mode ? self::$test_amount : $this->Amount * 100; 126 | 127 | // 3) Payment Informations 128 | 129 | $inputs['CustomerInvoiceRef'] = $this->ID; 130 | 131 | // 4) Customer Informations 132 | 133 | $inputs['CustomerFirstName'] = $data['FirstName']; 134 | $inputs['CustomerLastName'] = $data['Surname']; 135 | $inputs['CustomerEmail'] = $data['Email']; 136 | $inputs['CustomerAddress'] = $data['Address'] . ' ' . $data['AddressLine2'] . ' ' . $data['City'] . ' ' . $data['Country']; 137 | 138 | $inputs['CustomerPostcode'] = $member->hasMethod('getPostCode') ? $member->getPostCode() : ''; 139 | 140 | // 5) Empty Informations 141 | 142 | $inputs['CustomerInvoiceDescription'] = ''; 143 | $inputs['TrxnNumber'] = ''; 144 | $inputs['Option1'] = ''; 145 | $inputs['Option2'] = ''; 146 | $inputs['Option3'] = ''; 147 | 148 | // 6) Eway Payment Creation 149 | 150 | $customerID = self::$test_mode ? EWAY_DEFAULT_CUSTOMER_ID : self::$customer_id; 151 | $paymentMethod = self::$cvn_mode ? REAL_TIME_CVN : REAL_TIME; 152 | $eway = new EwayPaymentLive(); 153 | $eway->EwayPaymentLive($customerID, $paymentMethod, ! self::$test_mode); 154 | 155 | foreach ($inputs as $name => $value) { 156 | $eway->setTransactionData($name, $value); 157 | } 158 | 159 | // 7) Eway Transaction Sending 160 | 161 | $ewayResponseFields = $eway->doPayment(); 162 | 163 | // 8) Eway Response Management 164 | 165 | if ($ewayTrxnStatus = strtolower($ewayResponseFields['EWAYTRXNSTATUS'])) { 166 | $this->TxnRef = $ewayResponseFields['EWAYTRXNNUMBER']; 167 | $this->Message = $ewayResponseFields['EWAYTRXNERROR']; 168 | if ($ewayTrxnStatus == 'true') { 169 | $this->Status = 'Success'; 170 | $result = new Payment_Success(); 171 | } else { 172 | $this->Status = 'Failure'; 173 | $result = new Payment_Failure('The payment has not been completed correctly. An invalid response has been received from the Eway payment.'); 174 | } 175 | } else { 176 | $this->Status = 'Failure'; 177 | $result = new Payment_Failure('The payment has not been completed correctly. The payment request has not been sent to the Eway server correctly.'); 178 | } 179 | 180 | $this->write(); 181 | return $result; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /code/PayPalPayment/PayPalAdapter.php: -------------------------------------------------------------------------------- 1 | $formName(); 8 | } 9 | 10 | public function getPayPalFormFields() 11 | { 12 | $fields = $this->getBasicBrandFields(); 13 | } 14 | 15 | public function getPayPalFormRequirements() 16 | { 17 | } 18 | 19 | public function PayPalForm() 20 | { 21 | $fields =$this->getPayPalFormFields(); 22 | $requires = $this->getPayPalFormRequirements(); 23 | $customised = array(); 24 | $customised[] = $requires; 25 | 26 | $validator = new CustomRequiredFields($customised); 27 | $actions = new FieldSet( 28 | new FormAction('doAuthPayment', "Submit") 29 | ); 30 | $form = new Form(Controller::curr(), 'AuthForm', $fields, $actions, $validator); 31 | return $form; 32 | 33 | Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js'); 34 | 35 | // 1) Main Informations 36 | $fields = ''; 37 | $order = $this->Order(); 38 | $items = $order->Items(); 39 | $member = $order->Member(); 40 | 41 | // 2) Main Settings 42 | 43 | $url = self::$test_mode ? self::$test_url : self::$url; 44 | $inputs['cmd'] = '_cart'; 45 | $inputs['upload'] = '1'; 46 | 47 | // 3) Items Informations 48 | 49 | $cpt = 0; 50 | foreach ($items as $item) { 51 | $inputs['item_name_' . ++$cpt] = $item->TableTitle(); 52 | // item_number is unnecessary 53 | $inputs['amount_' . $cpt] = $item->UnitPrice(); 54 | $inputs['quantity_' . $cpt] = $item->Quantity; 55 | } 56 | 57 | // 4) Payment Informations And Authorisation Code 58 | 59 | $inputs['business'] = self::$test_mode ? self::$test_account_email : self::$account_email; 60 | $inputs['custom'] = $this->ID . '-' . $this->AuthorisationCode; 61 | // Add Here The Shipping And/Or Taxes 62 | $inputs['currency_code'] = $this->Currency; 63 | 64 | // 5) Redirection Informations 65 | 66 | $inputs['cancel_return'] = Director::absoluteBaseURL() . PayPalPayment_Handler::cancel_link($inputs['custom']); 67 | $inputs['return'] = Director::absoluteBaseURL() . PayPalPayment_Handler::complete_link(); 68 | $inputs['rm'] = '2'; 69 | // Add Here The Notify URL 70 | 71 | // 6) PayPal Pages Style Optional Informations 72 | 73 | if (self:: $continue_button_text) { 74 | $inputs['cbt'] = self::$continue_button_text; 75 | } 76 | 77 | if (self::$header_image_url) { 78 | $inputs['cpp_header_image'] = urlencode(self::$header_image_url); 79 | } 80 | if (self::$header_back_color) { 81 | $inputs['cpp_headerback_color'] = self::$header_back_color; 82 | } 83 | if (self::$header_border_color) { 84 | $inputs['cpp_headerborder_color'] = self::$header_border_color; 85 | } 86 | if (self::$payflow_color) { 87 | $inputs['cpp_payflow_color'] = self::$payflow_color; 88 | } 89 | if (self::$back_color) { 90 | $inputs['cs'] = self::$back_color; 91 | } 92 | if (self::$image_url) { 93 | $inputs['image_url'] = urlencode(self::$image_url); 94 | } 95 | if (self::$page_style) { 96 | $inputs['page_style'] = self::$page_style; 97 | } 98 | 99 | // 7) Prepopulating Customer Informations 100 | 101 | $inputs['first_name'] = $member->FirstName; 102 | $inputs['last_name'] = $member->Surname; 103 | $inputs['address1'] = $member->Address; 104 | $inputs['address2'] = $member->AddressLine2; 105 | $inputs['city'] = $member->City; 106 | $inputs['country'] = $member->Country; 107 | $inputs['email'] = $member->Email; 108 | 109 | if ($member->hasMethod('getState')) { 110 | $inputs['state'] = $member->getState(); 111 | } 112 | if ($member->hasMethod('getZip')) { 113 | $inputs['zip'] = $member->getZip(); 114 | } 115 | 116 | // 8) Form Creation 117 | if (is_array($inputs) && count($inputs)) { 118 | foreach ($inputs as $name => $value) { 119 | $ATT_value = Convert::raw2att($value); 120 | $fields .= ""; 121 | } 122 | } 123 | 124 | return << 126 | $fields 127 | 128 | 129 | 135 | HTML; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /code/PayPalPayment/PayPalPayment.php: -------------------------------------------------------------------------------- 1 | "HTMLText" 32 | * ); 33 | * 34 | * Have a PayPalPaymentPage.ss template or replace some code below 35 | * (search for renderWith). On the PayPalPaymentPage you can use 36 | * $PayPalInstructions (see above) 37 | * 38 | * @package payment 39 | */ 40 | class PayPalPayment extends Payment 41 | { 42 | public static $db = array( 43 | 'AuthorisationCode' => 'Text' 44 | ); 45 | 46 | // PayPal Informations 47 | 48 | protected static $privacy_link = 'https://www.paypal.com/us/cgi-bin/webscr?cmd=p/gen/ua/policy_privacy-outside'; 49 | 50 | protected static $logo = 'payment/images/payments/paypal.jpg'; 51 | 52 | // URLs 53 | 54 | protected static $url = 'https://www.paypal.com/cgi-bin/webscr'; 55 | 56 | protected static $test_url = 'https://www.sandbox.paypal.com/cgi-bin/webscr'; 57 | 58 | // Test Mode 59 | 60 | protected static $test_mode = false; 61 | 62 | protected static $test_account_email; 63 | 64 | public static function set_test_mode($test_account_email) 65 | { 66 | self::$test_mode = true; 67 | self::$test_account_email = $test_account_email; 68 | } 69 | 70 | // Payment Informations 71 | 72 | protected static $account_email; 73 | 74 | public static function set_account_email($account_email) 75 | { 76 | self::$account_email = $account_email; 77 | } 78 | 79 | // PayPal Pages Style Optional Informations 80 | 81 | protected static $continue_button_text; 82 | 83 | public static function set_continue_button_text($continue_button_text) 84 | { 85 | self::$continue_button_text = $continue_button_text; 86 | } 87 | 88 | protected static $header_image_url; 89 | 90 | public static function set_header_image_url($header_image_url) 91 | { 92 | self::$header_image_url = $header_image_url; 93 | } 94 | 95 | protected static $header_back_color; 96 | 97 | public static function set_header_back_color($header_back_color) 98 | { 99 | self::$header_back_color = $header_back_color; 100 | } 101 | 102 | protected static $header_border_color; 103 | 104 | public static function set_header_border_color($header_border_color) 105 | { 106 | self::$header_border_color = $header_border_color; 107 | } 108 | 109 | protected static $payflow_color; 110 | 111 | public static function set_payflow_color($payflow_color) 112 | { 113 | self::$payflow_color = $payflow_color; 114 | } 115 | 116 | protected static $back_color; 117 | 118 | public static function set_back_color_black() 119 | { 120 | self::$back_color = '1'; 121 | } 122 | 123 | protected static $image_url; 124 | 125 | public static function set_image_url($image_url) 126 | { 127 | self::$image_url = $image_url; 128 | } 129 | 130 | protected static $page_style; 131 | 132 | public static function set_page_style($page_style) 133 | { 134 | self::$page_style = $page_style; 135 | } 136 | 137 | public function getPaymentFormFields() 138 | { 139 | $logo = 'Credit card payments powered by PayPal'; 140 | $privacyLink = '' . $logo . '
'; 141 | return new FieldSet( 142 | new LiteralField('PayPalInfo', $privacyLink), 143 | new LiteralField( 144 | 'PayPalPaymentsList', 145 | 'Visa' . 146 | 'MasterCard' . 147 | 'American Express' . 148 | 'Discover' . 149 | 'PayPal' 150 | ) 151 | ); 152 | } 153 | 154 | public function getPaymentFormRequirements() 155 | { 156 | return null; 157 | } 158 | 159 | public function processPayment($data, $form) 160 | { 161 | $page = new Page(); 162 | 163 | $page->Title = 'Redirection to PayPal...'; 164 | $page->Logo = 'Payments powered by PayPal'; 165 | $page->Form = $this->PayPalForm(); 166 | 167 | $controller = new Page_Controller($page); 168 | 169 | $form = $controller->renderWith('PaymentProcessingPage'); 170 | 171 | return new Payment_Processing($form); 172 | } 173 | 174 | public function PayPalForm() 175 | { 176 | Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js'); 177 | 178 | // 1) Main Informations 179 | $fields = ''; 180 | $order = $this->Order(); 181 | $items = $order->Items(); 182 | $member = $order->Member(); 183 | 184 | // 2) Main Settings 185 | 186 | $url = self::$test_mode ? self::$test_url : self::$url; 187 | $inputs['cmd'] = '_cart'; 188 | $inputs['upload'] = '1'; 189 | 190 | // 3) Items Informations 191 | 192 | $cpt = 0; 193 | foreach ($items as $item) { 194 | $inputs['item_name_' . ++$cpt] = $item->TableTitle(); 195 | // item_number is unnecessary 196 | $inputs['amount_' . $cpt] = $item->UnitPrice(); 197 | $inputs['quantity_' . $cpt] = $item->Quantity; 198 | } 199 | 200 | // 4) Payment Informations And Authorisation Code 201 | 202 | $inputs['business'] = self::$test_mode ? self::$test_account_email : self::$account_email; 203 | $inputs['custom'] = $this->ID . '-' . $this->AuthorisationCode; 204 | // Add Here The Shipping And/Or Taxes 205 | $inputs['currency_code'] = $this->Currency; 206 | 207 | // 5) Redirection Informations 208 | 209 | $inputs['cancel_return'] = Director::absoluteBaseURL() . PayPalPayment_Handler::cancel_link($inputs['custom']); 210 | $inputs['return'] = Director::absoluteBaseURL() . PayPalPayment_Handler::complete_link(); 211 | $inputs['rm'] = '2'; 212 | // Add Here The Notify URL 213 | 214 | // 6) PayPal Pages Style Optional Informations 215 | 216 | if (self:: $continue_button_text) { 217 | $inputs['cbt'] = self::$continue_button_text; 218 | } 219 | 220 | if (self::$header_image_url) { 221 | $inputs['cpp_header_image'] = urlencode(self::$header_image_url); 222 | } 223 | if (self::$header_back_color) { 224 | $inputs['cpp_headerback_color'] = self::$header_back_color; 225 | } 226 | if (self::$header_border_color) { 227 | $inputs['cpp_headerborder_color'] = self::$header_border_color; 228 | } 229 | if (self::$payflow_color) { 230 | $inputs['cpp_payflow_color'] = self::$payflow_color; 231 | } 232 | if (self::$back_color) { 233 | $inputs['cs'] = self::$back_color; 234 | } 235 | if (self::$image_url) { 236 | $inputs['image_url'] = urlencode(self::$image_url); 237 | } 238 | if (self::$page_style) { 239 | $inputs['page_style'] = self::$page_style; 240 | } 241 | 242 | // 7) Prepopulating Customer Informations 243 | 244 | $inputs['first_name'] = $member->FirstName; 245 | $inputs['last_name'] = $member->Surname; 246 | $inputs['address1'] = $member->Address; 247 | $inputs['address2'] = $member->AddressLine2; 248 | $inputs['city'] = $member->City; 249 | $inputs['country'] = $member->Country; 250 | $inputs['email'] = $member->Email; 251 | 252 | if ($member->hasMethod('getState')) { 253 | $inputs['state'] = $member->getState(); 254 | } 255 | if ($member->hasMethod('getZip')) { 256 | $inputs['zip'] = $member->getZip(); 257 | } 258 | 259 | // 8) Form Creation 260 | if (is_array($inputs) && count($inputs)) { 261 | foreach ($inputs as $name => $value) { 262 | $ATT_value = Convert::raw2att($value); 263 | $fields .= ""; 264 | } 265 | } 266 | 267 | return << 269 | $fields 270 | 271 | 272 | 278 | HTML; 279 | } 280 | 281 | public function populateDefaults() 282 | { 283 | parent::populateDefaults(); 284 | $this->AuthorisationCode = md5(uniqid(rand(), true)); 285 | } 286 | } 287 | 288 | /** 289 | * Handler for responses from the PayPal site 290 | */ 291 | class PayPalPayment_Handler extends Controller 292 | { 293 | 294 | public static $URLSegment = 'paypal'; 295 | 296 | public static function complete_link() 297 | { 298 | return self::$URLSegment . '/complete'; 299 | } 300 | 301 | public static function cancel_link($custom) 302 | { 303 | return self::complete_link() . '?custom=' . $custom; 304 | } 305 | 306 | /** 307 | * Manages the 'return' and 'cancel' PayPal replies 308 | */ 309 | public function complete() 310 | { 311 | if (isset($_REQUEST['custom']) && $custom = $_REQUEST['custom']) { 312 | $params = explode('-', $custom); 313 | if (count($params) == 2) { 314 | if ($payment = DataObject::get_by_id('PayPalPayment', $params[0])) { 315 | if ($payment->AuthorisationCode == $params[1]) { 316 | if (isset($_REQUEST['payment_status']) && $_REQUEST['payment_status'] == 'Completed') { 317 | $payment->Status = 'Success'; 318 | $payment->TxnRef = $_REQUEST['txn_id']; 319 | } else { 320 | $payment->Status = 'Failure'; 321 | } 322 | 323 | $payment->write(); 324 | $payment->redirectToOrder(); 325 | } 326 | } 327 | } 328 | } 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /code/PayStation/PaystationHostedPayment.php: -------------------------------------------------------------------------------- 1 | '; 44 | $privacyLink = '' . $logo . '
'; 45 | return new FieldSet( 46 | new LiteralField('PaystationInfo', $privacyLink), 47 | new LiteralField( 48 | 'PaystationPaymentsList', 49 | 'Visa' . 50 | 'MasterCard' . 51 | 'American Express' 52 | ) 53 | ); 54 | } 55 | 56 | public function getPaymentFormRequirements() 57 | { 58 | return null; 59 | } 60 | 61 | public function processPayment($data, $form) 62 | { 63 | $page = new Page(); 64 | 65 | $page->Title = 'Redirection to Paystation...'; 66 | $page->Logo = 'Payments powered by Paystation'; 67 | $page->Form = $this->PaystationForm(); 68 | 69 | $controller = new Page_Controller($page); 70 | 71 | Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js'); 72 | 73 | $form = $controller->renderWith('PaymentProcessingPage'); 74 | 75 | return new Payment_Processing($form); 76 | } 77 | 78 | public function PaystationForm() 79 | { 80 | 81 | // 1) Main Informations 82 | 83 | $order = $this->Order(); 84 | $member = $order->Member(); 85 | 86 | // 2) Main Settings 87 | 88 | $amount = $order->Total() * 100; 89 | $url = sprintf(self::$url, self::$merchant_id, $this->ID, $amount); 90 | if (self::$test_mode) { 91 | $url .= '&tm=t'; 92 | } 93 | if (self::$merchant_ref) { 94 | $url .= '&merchant_ref=' . self::$merchant_ref; 95 | } 96 | $url = Convert::raw2xml($url); 97 | 98 | // 3) Form Creation 99 | 100 | return<< 102 | jQuery(document).ready(function() { 103 | location = "$url"; 104 | }); 105 | 106 | HTML; 107 | } 108 | } 109 | 110 | /** 111 | * Handler for responses from the PayPal site 112 | */ 113 | class PaystationHostedPayment_Handler extends Controller 114 | { 115 | 116 | public static $URLSegment = 'paystation'; 117 | 118 | public static function complete_link() 119 | { 120 | return self::$URLSegment . '/complete'; 121 | } 122 | 123 | public function complete() 124 | { 125 | if (isset($_REQUEST['ec'])) { 126 | if (isset($_REQUEST['ms'])) { 127 | if ($payment = DataObject::get_by_id('PaystationHostedPayment', $_REQUEST['ms'])) { 128 | $payment->Status = $_REQUEST['ec'] == '0' ? 'Success' : 'Failure'; 129 | if ($_REQUEST['ti']) { 130 | $payment->TxnRef = $_REQUEST['ti']; 131 | } 132 | if ($_REQUEST['em']) { 133 | $payment->Message = $_REQUEST['em']; 134 | } 135 | 136 | $payment->write(); 137 | 138 | $payment->redirectToOrder(); 139 | } else { 140 | user_error('There is no any Paystation hosted payment which ID is #' . $_REQUEST['ms'], E_USER_ERROR); 141 | } 142 | } else { 143 | user_error('There is no any Paystation hosted payment ID specified', E_USER_ERROR); 144 | } 145 | } else { 146 | user_error('There is no any Paystation hosted payment error code specified', E_USER_ERROR); 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /code/PayStation/PaystationPayment.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverstripe-archive/silverstripe-payment/9a0a040fda121d8d843eed846efc591ba543bfdc/code/PayStation/PaystationPayment.php -------------------------------------------------------------------------------- /code/PayerHavingReceipt.php: -------------------------------------------------------------------------------- 1 | array( 9 | 'Street' => 'Varchar', 10 | 'Suburb' => 'Varchar', 11 | 'CityTown' => 'Varchar', 12 | 'Country' => 'Varchar', 13 | ), 14 | ); 15 | } 16 | 17 | public function ReceiptMessage() 18 | { 19 | return $this->owner->renderWith('Payer_receipt'); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /code/Payment.php: -------------------------------------------------------------------------------- 1 | "Enum('Incomplete,Success,Failure,Pending','Incomplete')", 26 | 'Amount' => 'Money', 27 | 'Message' => 'Text', 28 | 'IP' => 'Varchar', 29 | 'ProxyIP' => 'Varchar', 30 | 'PaidForID' => "Int", 31 | 'PaidForClass' => 'Varchar', 32 | 33 | //This is used only when the payment is one of the recurring payments, when a scheduler is trying to 34 | //find which is the latest one for the recurring payments 35 | 'PaymentDate' => "Date", 36 | 37 | //Usered for store any Exception during this payment Process. 38 | 'ExceptionError' => 'Text' 39 | ); 40 | 41 | public static $has_one = array( 42 | 'RecurringPayment' => 'RecurringPayment', 43 | 'PaidBy' => 'Member', 44 | ); 45 | 46 | /** 47 | * Instances of Payment supported (usable) on this site. 48 | * @var array 49 | */ 50 | protected static $supported_methods = array( 51 | 'ChequePayment' => 'Cheque' 52 | ); 53 | 54 | 55 | /** 56 | * Make payment table transactional. 57 | */ 58 | public static $create_table_options = array( 59 | 'MySQLDatabase' => 'ENGINE=InnoDB' 60 | ); 61 | 62 | /** 63 | * The currency code used for payments. 64 | * @var string 65 | */ 66 | protected static $site_currency = 'USD'; 67 | 68 | /** 69 | * Set the currency code that this site uses. 70 | * @param string $currency Currency code. e.g. "NZD" 71 | */ 72 | public static function set_site_currency($currency) 73 | { 74 | self::$site_currency = $currency; 75 | } 76 | 77 | /** 78 | * Return the site currency in use. 79 | * @return string 80 | */ 81 | public static function site_currency() 82 | { 83 | return self::$site_currency; 84 | } 85 | 86 | /** 87 | * Set the payment types that this site supports. 88 | * The classes should all be subclasses of Payment. 89 | * 90 | * @param array $methodMap A map of class names to human-readable descriptions of the payment methods. 91 | */ 92 | public static function set_supported_methods($methodMap) 93 | { 94 | self::$supported_methods = $methodMap; 95 | } 96 | 97 | /** 98 | * @return array 99 | */ 100 | public static function get_supported_methods() 101 | { 102 | return self::$supported_methods; 103 | } 104 | 105 | public function populateDefaults() 106 | { 107 | parent::populateDefaults(); 108 | 109 | $this->Amount->Currency = Payment::site_currency(); 110 | $this->setClientIP(); 111 | } 112 | 113 | /** 114 | * Set the IP address of the user to this payment record. 115 | * This isn't perfect - IP addresses can be hidden fairly easily. 116 | */ 117 | public function setClientIP() 118 | { 119 | $proxy = null; 120 | $ip = null; 121 | 122 | if (isset($_SERVER['HTTP_CLIENT_IP'])) { 123 | $ip = $_SERVER['HTTP_CLIENT_IP']; 124 | } elseif (isset($_SERVER['REMOTE_ADDR'])) { 125 | $ip = $_SERVER['REMOTE_ADDR']; 126 | } else { 127 | $ip = null; 128 | } 129 | 130 | if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { 131 | $proxy = $ip; 132 | $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; 133 | } 134 | 135 | // Only set the IP and ProxyIP if none currently set 136 | if (!$this->IP) { 137 | $this->IP = $ip; 138 | } 139 | if (!$this->ProxyIP) { 140 | $this->ProxyIP = $proxy; 141 | } 142 | } 143 | 144 | /** 145 | * Returns the Payment type currently in use. 146 | * @return string 147 | */ 148 | public function PaymentMethod() 149 | { 150 | if (isset(self::$supported_methods[$this->ClassName])) { 151 | return self::$supported_methods[$this->ClassName]; 152 | } 153 | } 154 | 155 | /** 156 | * Return a set of payment fields from all enabled 157 | * payment methods for this site, given the . {@link Payment::set_supported_methods()} 158 | * is used to define which methods are available. 159 | * 160 | * @return FieldSet 161 | */ 162 | public static function combined_form_fields($amount) 163 | { 164 | 165 | // Create the initial form fields, which defines an OptionsetField 166 | // allowing the user to choose which payment method to use. 167 | $fields = new FieldSet( 168 | new HeaderField(_t('Payment.PAYMENTTYPE', 'Payment Type'), 3), 169 | new OptionsetField( 170 | 'PaymentMethod', 171 | '', 172 | self::$supported_methods, 173 | array_shift(array_keys(self::$supported_methods)) 174 | ) 175 | ); 176 | 177 | // If the user defined an numerically indexed array, throw an error 178 | if (ArrayLib::is_associative(self::$supported_methods)) { 179 | foreach (self::$supported_methods as $methodClass => $methodTitle) { 180 | 181 | // Create a new CompositeField with method specific fields, 182 | // as defined on each payment method class using getPaymentFormFields() 183 | $methodFields = new CompositeField(singleton($methodClass)->getPaymentFormFields()); 184 | $methodFields->setID("MethodFields_$methodClass"); 185 | $methodFields->addExtraClass('paymentfields'); 186 | 187 | // Add those fields to the initial FieldSet we first created 188 | $fields->push($methodFields); 189 | } 190 | } else { 191 | user_error('Payment::set_supported_methods() requires an associative array.', E_USER_ERROR); 192 | } 193 | 194 | // Add the amount and subtotal fields for the payment amount 195 | $fields->push(new ReadonlyField('Amount', _t('Payment.AMOUNT', 'Amount'), $amount)); 196 | 197 | return $fields; 198 | } 199 | 200 | /** 201 | * Return the form requirements for all the payment methods. 202 | * 203 | * @return An array suitable for passing to CustomRequiredFields 204 | */ 205 | public static function combined_form_requirements() 206 | { 207 | $requirements = array(); 208 | 209 | // Loop on available methods 210 | foreach (self::$supported_methods as $method => $methodTitle) { 211 | $methodRequirements = singleton($method)->getPaymentFormRequirements(); 212 | if ($methodRequirements) { 213 | // Put limiters into the JS/PHP code to only use those requirements for this payment method 214 | $methodRequirements['js'] = "for(var i=0; i <= this.elements.PaymentMethod.length-1; i++) " 215 | . "if(this.elements.PaymentMethod[i].value == '$method' && this.elements.PaymentMethod[i].checked == true) {" 216 | . $methodRequirements['js'] . " } "; 217 | 218 | $methodRequirements['php'] = "if(\$data['PaymentMethod'] == '$method') { " . 219 | $methodRequirements['php'] . " } "; 220 | 221 | $requirements[] = $methodRequirements; 222 | } 223 | } 224 | 225 | return $requirements; 226 | } 227 | 228 | /** 229 | * Return the payment form fields that should 230 | * be shown on the checkout order form for the 231 | * payment type. Example: for {@link DPSPayment}, 232 | * this would be a set of fields to enter your 233 | * credit card details. 234 | * 235 | * @return FieldSet 236 | */ 237 | public function getPaymentFormFields() 238 | { 239 | user_error("Please implement getPaymentFormFields() on $this->class", E_USER_ERROR); 240 | } 241 | 242 | /** 243 | * Define what fields defined in {@link Order->getPaymentFormFields()} 244 | * should be required. 245 | * 246 | * @see DPSPayment->getPaymentFormRequirements() for an example on how 247 | * this is implemented. 248 | * 249 | * @return array 250 | */ 251 | public function getPaymentFormRequirements() 252 | { 253 | user_error("Please implement getPaymentFormRequirements() on $this->class", E_USER_ERROR); 254 | } 255 | 256 | /** 257 | * Perform payment processing for the type of 258 | * payment. For example, if this was a credit card 259 | * payment type, you would perform the data send 260 | * off to the payment gateway on this function for 261 | * your payment subclass. 262 | * 263 | * This is used by {@link OrderForm} when it is 264 | * submitted. 265 | * 266 | * @param array $data The form request data - see OrderForm 267 | * @param OrderForm $form The form object submitted on 268 | */ 269 | public function processPayment($data, $form) 270 | { 271 | user_error("Please implement processPayment() on $this->class", E_USER_ERROR); 272 | } 273 | 274 | public function getForm($whichTest) 275 | { 276 | user_error("Please implement getForm() on $this->class", E_USER_ERROR); 277 | } 278 | 279 | public function payAsRecurring() 280 | { 281 | user_error("Please implement payAsRecurring() on $this->class", E_USER_ERROR); 282 | } 283 | 284 | public function handleError($e) 285 | { 286 | $this->ExceptionError = $e->getMessage(); 287 | $this->write(); 288 | } 289 | 290 | public function PaidObject() 291 | { 292 | return DataObject::get_by_id($this->PaidForClass, $this->PaidForID); 293 | } 294 | } 295 | abstract class Payment_Result 296 | { 297 | 298 | protected $value; 299 | 300 | public function __construct($value = null) 301 | { 302 | $this->value = $value; 303 | } 304 | 305 | public function getValue() 306 | { 307 | return $this->value; 308 | } 309 | 310 | abstract public function isSuccess(); 311 | 312 | abstract public function isProcessing(); 313 | } 314 | class Payment_Success extends Payment_Result 315 | { 316 | 317 | public function isSuccess() 318 | { 319 | return true; 320 | } 321 | 322 | public function isProcessing() 323 | { 324 | return false; 325 | } 326 | } 327 | class Payment_Processing extends Payment_Result 328 | { 329 | 330 | public function isSuccess() 331 | { 332 | return false; 333 | } 334 | 335 | public function isProcessing() 336 | { 337 | return true; 338 | } 339 | } 340 | class Payment_Failure extends Payment_Result 341 | { 342 | 343 | public function isSuccess() 344 | { 345 | return false; 346 | } 347 | 348 | public function isProcessing() 349 | { 350 | return false; 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /code/RecurringPayment.php: -------------------------------------------------------------------------------- 1 | "Enum('Incomplete,Success,Failure,Pending','Incomplete')", 7 | 'Amount' => 'Money', 8 | 'Message' => 'Text', 9 | 'IP' => 'Varchar', 10 | 'ProxyIP' => 'Varchar', 11 | 'PaidForID' => "Int", 12 | 'PaidForClass' => 'Varchar', 13 | 14 | //The following fields store the recurring payment schedulling info 15 | 'Frequency' => "Enum('Weekly,Monthly,Yearly','Monthly')", 16 | 'StartingDate' => "Date", 17 | 'Times' => "Int", 18 | 19 | //Usered for store any Exception during this payment Process. 20 | 'ExceptionError' => 'Text' 21 | ); 22 | 23 | public static $has_one = array( 24 | 'PaidBy' => 'Member' 25 | ); 26 | 27 | public static $has_many = array( 28 | 'Payments' => "Payment" 29 | ); 30 | 31 | public static $create_table_options = array( 32 | 'MySQLDatabase' => 'ENGINE=InnoDB' 33 | ); 34 | 35 | public function populateDefaults() 36 | { 37 | parent::populateDefaults(); 38 | 39 | $this->Amount->Currency = Payment::site_currency(); 40 | $this->setClientIP(); 41 | } 42 | 43 | /** 44 | * Set the IP address of the user to this payment record. 45 | * This isn't perfect - IP addresses can be hidden fairly easily. 46 | */ 47 | public function setClientIP() 48 | { 49 | $proxy = null; 50 | $ip = null; 51 | 52 | if (isset($_SERVER['HTTP_CLIENT_IP'])) { 53 | $ip = $_SERVER['HTTP_CLIENT_IP']; 54 | } elseif (isset($_SERVER['REMOTE_ADDR'])) { 55 | $ip = $_SERVER['REMOTE_ADDR']; 56 | } else { 57 | $ip = null; 58 | } 59 | 60 | if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { 61 | $proxy = $ip; 62 | $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; 63 | } 64 | 65 | // Only set the IP and ProxyIP if none currently set 66 | if (!$this->IP) { 67 | $this->IP = $ip; 68 | } 69 | if (!$this->ProxyIP) { 70 | $this->ProxyIP = $proxy; 71 | } 72 | } 73 | 74 | public function CanPayNext() 75 | { 76 | $success = $this->Status = 'Success'; 77 | if ($this->Times && $successPayments = $this->SuccessPayments()) { 78 | return $success && $this->Times > $successPayments->count(); 79 | } else { 80 | return $success; 81 | } 82 | } 83 | 84 | public function SuccessPayments() 85 | { 86 | return DataObject::get("Payment", "\"RecurringPaymentID\" = '".(int)$this->ID."' AND \"Status\" = 'Success'", "\"PaymentDate\" DESC"); 87 | } 88 | 89 | public function Payments() 90 | { 91 | return DataObject::get("Payment", "\"RecurringPaymentID\" = '".(int)$this->ID."'"); 92 | } 93 | 94 | public function getNextPayment() 95 | { 96 | if ($latest = $this->getLatestPayment()) { 97 | $next = $this->generateNextPaymentFrom($latest); 98 | } else { 99 | $next = $this->generateFirstPayment(); 100 | } 101 | DB::getConn()->transactionSavepoint("NextPaymentGot"); 102 | return $next; 103 | } 104 | 105 | public function getLatestPayment($successonly = true) 106 | { 107 | if ($successonly) { 108 | return DataObject::get_one("Payment", "\"RecurringPaymentID\" = '".(int)$this->ID."' AND \"Status\" = 'Success'", false, "\"PaymentDate\" DESC"); 109 | } else { 110 | return DataObject::get_one("Payment", "\"RecurringPaymentID\" = '".(int)$this->ID."'", false, "\"PaymentDate\" DESC, \"LastEdited\" DESC"); 111 | } 112 | } 113 | 114 | public function payNext() 115 | { 116 | DB::getConn()->transactionStart(); 117 | try { 118 | if ($next = $this->getNextPayment()) { 119 | $next->payAsRecurring(); 120 | } 121 | DB::getConn()->transactionEnd(); 122 | } catch (Exception $e) { 123 | DB::getConn()->transactionRollback('NextPaymentGot'); 124 | DB::getConn()->transactionEnd(); 125 | $latestPayment = $this->getLatestPayment($successonly = false); 126 | $latestPayment->handleError($e); 127 | } 128 | } 129 | 130 | public function generateFirstPayment() 131 | { 132 | $payment = new Payment(); 133 | $payment->RecurringPaymentID = $this->ID; 134 | $payment->PaymentDate = $this->StartingDate; 135 | $payment->Amount->Amount = $this->Amount->Amount; 136 | $payment->Amount->Currency = $this->Amount->Currency; 137 | $payment->write(); 138 | return $payment; 139 | } 140 | 141 | public function generateNextPaymentFrom($latest) 142 | { 143 | switch ($this->Frequency) { 144 | case 'Weekly': 145 | $date = date('Y-m-d', strtotime('+1 week', strtotime($latest->PaymentDate))); 146 | break; 147 | case 'Monthly': 148 | $date = date('Y-m-d', strtotime('+1 month', strtotime($latest->PaymentDate))); 149 | break; 150 | case 'Yearly': 151 | $date = date('Y-m-d', strtotime('+1 year', strtotime($latest->PaymentDate))); 152 | break; 153 | } 154 | 155 | $payment = new Payment(); 156 | $payment->RecurringPaymentID = $this->ID; 157 | $payment->PaymentDate = $date; 158 | $payment->Amount->Amount = $this->Amount->Amount; 159 | $payment->Amount->Currency = $this->Amount->Currency; 160 | $payment->write(); 161 | return $payment; 162 | } 163 | 164 | public function handleError($e) 165 | { 166 | $this->ExceptionError = $e->getMessage(); 167 | $this->write(); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /code/Worldpay/WorldpayPayment.php: -------------------------------------------------------------------------------- 1 | '; 126 | $privacyLink = '' . $logo . '
'; 127 | return new FieldSet( 128 | new LiteralField('WorldPayInfo', $privacyLink), 129 | new LiteralField( 130 | 'WorldPayPaymentsList', 131 | 'Visa' . 132 | 'MasterCard' . 133 | 'American Express' . 134 | 'Dinners Club' . 135 | 'JCB' 136 | ) 137 | ); 138 | } 139 | 140 | public function getPaymentFormRequirements() 141 | { 142 | return null; 143 | } 144 | 145 | public function processPayment($data, $form) 146 | { 147 | $page = new Page(); 148 | 149 | $page->Title = 'Redirection to WorldPay...'; 150 | $page->Logo = 'Payments powered by WorldPay'; 151 | $page->Form = $this->WorldPayForm(); 152 | 153 | $controller = new Page_Controller($page); 154 | 155 | Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js'); 156 | 157 | $form = $controller->renderWith('PaymentProcessingPage'); 158 | 159 | return new Payment_Processing($form); 160 | } 161 | 162 | public function WorldPayForm() 163 | { 164 | 165 | // 1) Main Informations 166 | 167 | $order = $this->Order(); 168 | $items = $order->Items(); 169 | $member = $order->Member(); 170 | 171 | // 2) Mandatory Settings 172 | 173 | $inputs['instId'] = self::$installation_id; 174 | $inputs['cartId'] = $order->ID; 175 | $inputs['amount'] = $order->Total(); 176 | $inputs['currency'] = $this->Currency; 177 | 178 | // 3) Payment And Items Informations 179 | 180 | $inputs['MC_paymentID'] = $this->ID; 181 | $inputs['desc'] = 'Order made on ' . DBField::create('SSDatetime', $order->Created)->Long() . ' by ' . $member->FirstName . ' ' . $member->Surname; 182 | 183 | // 4) Test Mode And Customer Name Settings 184 | 185 | if (self::$test_mode) { 186 | $url = self::$test_url; 187 | $inputs['testMode'] = self::$test_mode; 188 | $inputs['name'] = self::$test_mode_name; 189 | } else { 190 | $url = self::$url; 191 | $inputs['name'] = $member->FirstName . ' ' . $member->Surname; 192 | } 193 | 194 | // 5) Redirection Informations 195 | 196 | $inputs['MC_callback'] = Director::absoluteBaseURL() . WorldpayPayment_Handler::complete_link(); 197 | 198 | // 6) Optional Payments And Authorisation Settings 199 | 200 | if (self::$result_file) { 201 | $inputs['resultFile'] = self::$result_file; 202 | } 203 | if (self::$merchant_code) { 204 | $inputs['accId'] = self::$merchant_code; 205 | } // Not sure about its exact syntax 206 | if (self::$authorisation_mode) { 207 | $inputs['authMode'] = self::$authorisation_mode; 208 | } 209 | if (self::$authorisation_valid_from) { 210 | $inputs['authValidFrom'] = self::$authorisation_valid_from; 211 | } 212 | if (self::$authorisation_valid_to) { 213 | $inputs['authValidTo'] = self::$authorisation_valid_to; 214 | } 215 | 216 | // 7) Prepopulating Customer Informations 217 | 218 | $inputs['address'] = $member->Address . ' ' . $member->AddressLine2 . ' ' . $member->City; 219 | $inputs['country'] = $member->Country; 220 | $inputs['email'] = $member->Email; 221 | 222 | if ($member->hasMethod('getPostCode')) { 223 | $inputs['postcode'] = $member->getPostCode(); 224 | } 225 | if ($member->hasMethod('getFax')) { 226 | $inputs['fax'] = $member->getFax(); 227 | } 228 | //$inputs['tel'] = $member->HomePhone; 229 | 230 | // 8) Form Creation 231 | 232 | foreach ($inputs as $name => $value) { 233 | $fields .= ''; 234 | } 235 | 236 | return <<$fields 238 | 243 | HTML; 244 | } 245 | } 246 | 247 | /** 248 | * Handler for responses from the WorldPay site 249 | */ 250 | class WorldpayPayment_Handler extends Controller 251 | { 252 | 253 | public static $URLSegment = 'worldpay'; 254 | 255 | public static function complete_link() 256 | { 257 | return self::$URLSegment . '/complete'; 258 | } 259 | 260 | /** 261 | * Get the Order object to modify, check security that it's the object you want to modify based 262 | * off Worldpay confirmation, update the Order object to show complete and Payment object to show 263 | * that it was received. Finally, send a receipt to the buyer to show these details. 264 | */ 265 | public function paid() 266 | { 267 | // Check if callback password is the same, otherwise fail 268 | if ($_REQUEST['callbackPW'] == WorldpayPayment::$callback_password) { 269 | $paymentID = $_REQUEST['MC_paymentID']; 270 | if (is_numeric($paymentID)) { 271 | if ($payment = DataObject::get_by_id('WorldpayPayment', $paymentID)) { 272 | if ($_REQUEST['transStatus'] == "Y") { 273 | $payment->Status = 'Success'; 274 | } else { 275 | $payment->Status = 'Failure'; 276 | } 277 | $payment->write(); 278 | $payment->redirectToOrder(); 279 | } else { 280 | USER_ERROR("CheckoutPage::OrderConfirmed - There is no Payment object for this order object (Order ID ".$orderID.")", E_USER_WARNING); 281 | } 282 | } else { 283 | USER_ERROR('CheckoutPage::OrderConfirmed - Order ID is NOT numeric', E_USER_WARNING); 284 | } 285 | } else { 286 | USER_ERROR("CheckoutPage::OrderConfirmed - Order error - password failed", E_USER_WARNING); 287 | } 288 | return; 289 | } 290 | 291 | public function complete() 292 | { 293 | $paymentID = $_REQUEST['MC_paymentID']; 294 | $payment = DataObject::get_by_id('WorldpayPayment', $paymentID); 295 | $payment->TxnRef = $_REQUEST['transId']; 296 | $transactionStatus = $_REQUEST['transStatus']; 297 | if ($transactionStatus == 'Y') { 298 | $payment->Status = 'Success'; 299 | } // Successful 300 | else { 301 | $payment->Status = 'Failure'; 302 | } // Cancelled 303 | $payment->Message = $_REQUEST['rawAuthMessage']; 304 | $payment->write(); 305 | $payment->redirectToOrder(); 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "silverstripe/payment", 3 | "description": "SilverStripe payment provider integration. Currently supported: DPS, Eway, Paypal, Paystation, Worldpay", 4 | "type": "silverstripe-module", 5 | "keywords": ["silverstripe", "payment", "ecommerce", "paypal", "worldpay", "dps", "eway", "paystation"], 6 | "authors": [ 7 | { 8 | "name": "Normann Lou", 9 | "email": "normann@silverstripe.com" 10 | } 11 | ], 12 | "require": 13 | { 14 | "silverstripe/framework": "~2.3", 15 | "silverstripe/cms": "~2.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /images/payments/dps.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverstripe-archive/silverstripe-payment/9a0a040fda121d8d843eed846efc591ba543bfdc/images/payments/dps.gif -------------------------------------------------------------------------------- /images/payments/eway.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverstripe-archive/silverstripe-payment/9a0a040fda121d8d843eed846efc591ba543bfdc/images/payments/eway.gif -------------------------------------------------------------------------------- /images/payments/methods/american-express.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverstripe-archive/silverstripe-payment/9a0a040fda121d8d843eed846efc591ba543bfdc/images/payments/methods/american-express.gif -------------------------------------------------------------------------------- /images/payments/methods/dinners-club.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverstripe-archive/silverstripe-payment/9a0a040fda121d8d843eed846efc591ba543bfdc/images/payments/methods/dinners-club.jpg -------------------------------------------------------------------------------- /images/payments/methods/discover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverstripe-archive/silverstripe-payment/9a0a040fda121d8d843eed846efc591ba543bfdc/images/payments/methods/discover.jpg -------------------------------------------------------------------------------- /images/payments/methods/jcb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverstripe-archive/silverstripe-payment/9a0a040fda121d8d843eed846efc591ba543bfdc/images/payments/methods/jcb.jpg -------------------------------------------------------------------------------- /images/payments/methods/mastercard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverstripe-archive/silverstripe-payment/9a0a040fda121d8d843eed846efc591ba543bfdc/images/payments/methods/mastercard.jpg -------------------------------------------------------------------------------- /images/payments/methods/paypal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverstripe-archive/silverstripe-payment/9a0a040fda121d8d843eed846efc591ba543bfdc/images/payments/methods/paypal.jpg -------------------------------------------------------------------------------- /images/payments/methods/visa.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverstripe-archive/silverstripe-payment/9a0a040fda121d8d843eed846efc591ba543bfdc/images/payments/methods/visa.jpg -------------------------------------------------------------------------------- /images/payments/paypal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverstripe-archive/silverstripe-payment/9a0a040fda121d8d843eed846efc591ba543bfdc/images/payments/paypal.jpg -------------------------------------------------------------------------------- /images/payments/paystation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverstripe-archive/silverstripe-payment/9a0a040fda121d8d843eed846efc591ba543bfdc/images/payments/paystation.jpg -------------------------------------------------------------------------------- /images/payments/worldpay.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverstripe-archive/silverstripe-payment/9a0a040fda121d8d843eed846efc591ba543bfdc/images/payments/worldpay.gif -------------------------------------------------------------------------------- /images/ui-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverstripe-archive/silverstripe-payment/9a0a040fda121d8d843eed846efc591ba543bfdc/images/ui-bg.jpg -------------------------------------------------------------------------------- /javascript/Eway.js: -------------------------------------------------------------------------------- 1 | Behaviour.register({ 2 | '#Eway_CreditCardType select' : { 3 | initialise : function() {hideShowCVN(this);}, 4 | onchange : function() {hideShowCVN(this);} 5 | } 6 | }); 7 | 8 | function hideShowCVN(element) { 9 | var display = 'none'; 10 | if(isCvnCreditCard(element.value)) display = 'block'; 11 | $('Eway_CreditCardCVN').style.display = display; 12 | } 13 | 14 | function isCvnCreditCard(type) { 15 | return type == 'VISA' || type == 'MASTERCARD' || type == 'AMEX'; 16 | } -------------------------------------------------------------------------------- /lang/_manifest_exclude: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/silverstripe-archive/silverstripe-payment/9a0a040fda121d8d843eed846efc591ba543bfdc/lang/_manifest_exclude -------------------------------------------------------------------------------- /lang/en_US.php: -------------------------------------------------------------------------------- 1 | 2 | <% control AuthPayment %> 3 |

$ClassName $ID

4 | <% if ExceptionError %> 5 |
$ExceptionError
6 | <% else %> 7 |
    8 |
  • Status: $Status
  • 9 | <% if TxnRef %>
  • TxnRef: $TxnRef
  • <% end_if %> 10 |
  • Type: $TxnType
  • 11 |
  • Amount: $Amount.Amount
  • 12 |
  • Currency: $Amount.Currency
  • 13 | <% if AuthCode %>
  • AuthCode: $AuthCode
  • <% end_if %> 14 | <% if Message %>
  • Message: $Message
  • <% end_if %> 15 | <% if PaymentDate %>
  • Payment Date: $PaymentDate
  • <% end_if %> 16 |
17 | <% end_if %> 18 | <% end_control %> 19 | <% end_if %> 20 | 21 | <% if TxnType=Refund %> 22 | <% control RefundedFor %> 23 |

$ClassName $ID

24 | <% if ExceptionError %> 25 |
$ExceptionError
26 | <% else %> 27 |
    28 |
  • Status: $Status
  • 29 | <% if TxnRef %>
  • TxnRef: $TxnRef
  • <% end_if %> 30 |
  • Type: $TxnType
  • 31 |
  • Amount: $Amount.Amount
  • 32 |
  • Currency: $Amount.Currency
  • 33 | <% if AuthCode %>
  • AuthCode: $AuthCode
  • <% end_if %> 34 | <% if Message %>
  • Message: $Message
  • <% end_if %> 35 | <% if PaymentDate %>
  • Payment Date: $PaymentDate
  • <% end_if %> 36 |
37 | <% end_if %> 38 | <% end_control %> 39 | <% end_if %> 40 | 41 |

$ClassName $ID

42 | <% if ExceptionError %> 43 |
$ExceptionError
44 | <% else %> 45 |
    46 |
  • Status: $Status
  • 47 | <% if TxnRef %>
  • TxnRef: $TxnRef
  • <% end_if %> 48 |
  • Type: $TxnType
  • 49 |
  • Amount: $Amount.Amount
  • 50 |
  • Currency: $Amount.Currency
  • 51 | <% if AuthCode %>
  • AuthCode: $AuthCode
  • <% end_if %> 52 | <% if Message %>
  • Message: $Message
  • <% end_if %> 53 | <% if PaymentDate %>
  • Payment Date: $PaymentDate
  • <% end_if %> 54 |
55 | <% end_if %> 56 | -------------------------------------------------------------------------------- /templates/Includes/DPSPayment_receipt.ss: -------------------------------------------------------------------------------- 1 |

Payment Receipt (Ref no. #$ID)

2 |
3 | <% if MerchantReference %>

$MerchantReference

<% end_if %> 4 | 5 | 6 | 7 | <% if TxnRef %><% end_if %> 8 | 9 | 10 | <% if PaymentDate %><% end_if %> 11 | 12 |
Status:$Status
DPS Reference:$TxnRef
Payment Type:$TxnType
Paid Amount:$Amount.Nice ($Amount.Currency)
Payment Date:$PaymentDate.Nice
13 | -------------------------------------------------------------------------------- /templates/Includes/Payer_receipt.ss: -------------------------------------------------------------------------------- 1 |

Paid By:

2 | 3 | 4 | 5 | 6 | 7 | 8 | <% if Street %><% end_if %> 9 | <% if Suburb %><% end_if %> 10 | <% if CityTown %><% end_if %> 11 | <% if Country %><% end_if %> 12 | 13 |
First Name:$FirstName
Last Name:$Surname
Email:$Email
Street:$Street
Suburb:$Suburb
CityTown:$CityTown
Country:$Country
--------------------------------------------------------------------------------